🧬
EPISODE 05
<T> · 제약 · keyof · Partial/Pick/Omit/Record
제네릭
타입을 매개변수처럼 사용하는 제네릭으로 재사용 가능한 함수·인터페이스·클래스를 작성하고, keyof와 유틸리티 타입(Partial/Pick/Omit/Record)을 활용합니다.
genericskeyofutility types
소요 시간
⏱ 45~60분
난이도
📊 중급
선수 조건
🎯 ts-04
결과물
ApiResponse<T>, Stack<T> 같은 재사용 가능한 타입 작성
이 강의에서 배우는 것
- 1제네릭 함수/인터페이스/클래스를 정의한다
- 2T extends ... 로 제네릭 제약을 건다
- 3keyof로 객체의 키 타입을 추출한다
- 4Partial/Pick/Omit/Record/ReturnType 같은 유틸리티를 활용한다
1. 제네릭이란
typescript
// 제네릭 없이
function getFirstNumber(arr: number[]): number { return arr[0]; }
function getFirstString(arr: string[]): string { return arr[0]; }
// 제네릭
function getFirst<T>(arr: T[]): T { return arr[0]; }
getFirst<number>([1, 2, 3]); // T = number
getFirst<string>(['a', 'b']); // T = string
getFirst([true, false]); // T = boolean (추론)2. 제네릭 인터페이스
typescript
interface ApiResponse<T> {
success: boolean;
data: T;
message?: string;
timestamp: string;
}
const userResponse: ApiResponse<User> = {
success: true,
data: { id: 1, name: '홍길동' },
timestamp: new Date().toISOString(),
};
const listResponse: ApiResponse<Product[]> = {
success: true, data: [...products], timestamp: '...',
};3. 제네릭 클래스
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]; }
get size(): number { return this.items.length; }
}
const numberStack = new Stack<number>();
const stringStack = new Stack<string>();4. 제약 (extends)
typescript
// T는 반드시 { name: string } 형태
function printName<T extends { name: string }>(item: T): void {
console.log(item.name);
}
printName({ name: '홍길동', age: 30 }); // OK
printName({ age: 30 }); // 오류: name 없음5. keyof
typescript
interface User {
id: number;
name: string;
email: string;
}
type UserKey = keyof User; // 'id' | 'name' | 'email'
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: '홍길동', email: 'hong@…' };
getProperty(user, 'name'); // string
getProperty(user, 'id'); // number
// getProperty(user, 'age'); // 오류6. 유틸리티 타입
typescript
interface User {
id: number; name: string; email: string; password: string;
}
// Partial — 모든 프로퍼티 선택적
type PartialUser = Partial<User>;
// Required — 모든 프로퍼티 필수
type RequiredUser = Required<PartialUser>;
// Pick — 특정 프로퍼티만
type PublicUser = Pick<User, 'id' | 'name' | 'email'>;
// Omit — 특정 프로퍼티 제외
type SafeUser = Omit<User, 'password'>;
// Record — 키-값 매핑
type RolePermissions = Record<'admin' | 'user' | 'guest', string[]>;
// ReturnType — 함수 반환 타입 추출
function createUser() { return { id: 1, name: '홍길동' }; }
type CreatedUser = ReturnType<typeof createUser>;