← 홈페이지 5강 목록으로
🧬
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>;
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗