[2026] TypeScript Generics | Complete Guide
이 글의 핵심
Generics in TypeScript: typed identity functions, generic functions and classes, constraints with extends and keyof, caches, and common mistakes—tutorial for reusable safe APIs.
Introduction
What are generics?
Generics let you treat types like parameters so you can build reusable, type-safe components.
1. Generics basics
The problem
If a function must accept many types, using any throws away safety:
아래 코드는 typescript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// any — loses type safety
function identity(value: any): any {
return value;
}
const result1 = identity("hello"); // any
const result2 = identity(123); // any
// Issues:
// 1. Return type is any — no checking
// 2. result1.toFixed() might compile but fail at runtime
// 3. Input/output relationship is not expressed
The generic solution
다음은 typescript를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// <T> declares a type parameter (T is conventional)
function identity<T>(value: T): T {
return value;
}
const result1 = identity<string>("hello");
const result2 = identity<number>(123);
// Inference (preferred)
const result3 = identity("hello"); // T inferred as string
const result4 = identity(123); // T inferred as number
// Now the compiler preserves accuracy
// result3.toUpperCase(); // ✅
// result3.toFixed(); // ❌ error
// result4.toFixed(2); // ✅
Benefits:
- Safety: checked at compile time
- Reuse: one implementation for many types
- Clarity: documents type relationships
- Tooling: better autocomplete
Generics vs any
| Aspect | Generics (
<T>) |any| |--------|------------------|-------| | Safety | ✅ | ❌ | | Inference | ✅ | ❌ | | Autocomplete | ✅ | ❌ | | Runtime surprises | ✅ reduced | ❌ likely |
2. Generic functions
Basics
아래 코드는 typescript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
function getFirstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const numbers = [1, 2, 3];
const first = getFirstElement(numbers);
const strings = ["a", "b", "c"];
const firstStr = getFirstElement(strings);
Multiple type parameters
아래 코드는 typescript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const result1 = pair("hello", 123);
const result2 = pair(true, "world");
Arrow functions
아래 코드는 typescript를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
const map = <T, U>(arr: T[], fn: (item: T) => U): U[] => {
return arr.map(fn);
};
const numbers = [1, 2, 3];
const doubled = map(numbers, (n) => n * 2);
const strings = map(numbers, (n) => n.toString());
3. Generic interfaces
Basics
아래 코드는 typescript를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
interface Box<T> {
value: T;
}
const numberBox: Box<number> = { value: 123 };
const stringBox: Box<string> = { value: "hello" };
const boxOfBoxes: Box<Box<number>> = {
value: { value: 123 }
};
Example: API responses
다음은 typescript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
interface ApiResponse<T> {
success: boolean;
data: T;
error?: string;
}
interface User {
id: string;
name: string;
email: string;
}
interface Product {
id: string;
name: string;
price: number;
}
const userResponse: ApiResponse<User> = {
success: true,
data: {
id: "U001",
name: "Alice",
email: "alice@test.com"
}
};
const productResponse: ApiResponse<Product[]> = {
success: true,
data: [
{ id: "P001", name: "Laptop", price: 1000000 },
{ id: "P002", name: "Mouse", price: 30000 }
]
};
4. Generic classes
Basics
Generic classes are ideal for reusable data structures: 다음은 typescript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
size(): number {
return this.items.length;
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
console.log(numberStack.pop());
console.log(numberStack.peek());
// numberStack.push("hello"); // ❌ error
const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
console.log(stringStack.pop());
// stringStack.push(123); // ❌ error
Why generic classes help:
- One class for many element types
- No duplicate
StackNumber,StackString, etc. - Prevents mixing wrong types in the same stack
const taskStack = new Stack<Task>();
const undoStack = new Stack<Action>();
const historyStack = new Stack<string>();
5. Generic constraints
extends
아래 코드는 typescript를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(value: T): void {
console.log(value.length);
}
logLength("hello"); // ✅
logLength([1, 2, 3]); // ✅
// logLength(123); // ❌ number has no length
keyof
아래 코드는 typescript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = {
name: "Alice",
age: 25,
email: "alice@test.com"
};
const name = getProperty(user, "name");
const age = getProperty(user, "age");
// const invalid = getProperty(user, "invalid"); // ❌ error
6. Hands-on examples
Example 1: Chunk arrays
아래 코드는 typescript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
function chunk<T>(arr: T[], size: number): T[][] {
const result: T[][] = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
const numbers = [1, 2, 3, 4, 5, 6];
console.log(chunk(numbers, 2));
const strings = ["a", "b", "c", "d"];
console.log(chunk(strings, 3));
Example 2: Key–value cache
다음은 typescript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Cache<K, V> {
private store = new Map<K, V>();
set(key: K, value: V): void {
this.store.set(key, value);
}
get(key: K): V | undefined {
return this.store.get(key);
}
has(key: K): boolean {
return this.store.has(key);
}
delete(key: K): boolean {
return this.store.delete(key);
}
clear(): void {
this.store.clear();
}
}
const userCache = new Cache<string, User>();
userCache.set("U001", { id: "U001", name: "Alice", email: "alice@test.com" });
const user = userCache.get("U001");
console.log(user?.name);
Example 3: Promise wrapper
다음은 typescript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class AsyncResult<T> {
constructor(private promise: Promise<T>) {}
async map<U>(fn: (value: T) => U): Promise<AsyncResult<U>> {
const value = await this.promise;
return new AsyncResult(Promise.resolve(fn(value)));
}
async flatMap<U>(fn: (value: T) => Promise<U>): Promise<AsyncResult<U>> {
const value = await this.promise;
return new AsyncResult(fn(value));
}
async unwrap(): Promise<T> {
return await this.promise;
}
}
const result = new AsyncResult(Promise.resolve(10));
result
.map((x) => x * 2)
.then((r) => r.unwrap())
.then((value) => console.log(value)); // 20
7. Advanced patterns
Conditional types
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
Mapped types (preview)
아래 코드는 typescript를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number; }
8. Common mistakes
Mistake 1: Accessing properties without a constraint
아래 코드는 typescript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ Wrong
function getLength<T>(value: T): number {
return value.length; // T might not have length
}
// ✅ Correct
function getLength<T extends { length: number }>(value: T): number {
return value.length;
}
Mistake 2: Unnecessary generics
아래 코드는 typescript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ Generic adds nothing
function log<T>(message: string): void {
console.log(message);
}
// ✅ Simpler
function log(message: string): void {
console.log(message);
}
Summary
Takeaways
- Generics: parameterize types
- Functions:
function fn<T>(value: T): T - Interfaces:
interface Box<T> - Classes:
class Stack<T> - Constraints:
<T extends SomeType> - keyof: keys of object types