TypeScript는 대규모 웹 개발의 업계 표준이 되었습니다. 대부분의 개발자가 인터페이스와 타입의 기본을 알고 있지만, 진정한 힘은 고급 타입 시스템에 있습니다. 여기 시니어 엔지니어로서 당신을 차별화할 5가지 패턴이 있습니다.
1. Generic Constraints
Generics는 강력하지만, 때로는 전달될 수 있는 것을 제한해야 합니다. extends가 여기서 여러분의 친구입니다.
interface HasId { id: string; } function getById<T extends HasId>(list: T[], id: string): T | undefined { return list.find((item) => item.id === id); }
T가 HasId를 확장하도록 보장함으로써, 함수 내부에서 .id에 접근하는 것이 안전하다는 것을 보장합니다.
2. Conditional Types
조건부 타입은 비균일 타입 매핑을 생성할 수 있게 해줍니다. 구문은 JavaScript의 삼항 연산자와 유사합니다.
type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false
실용적인 사용 사례는 유니온에서 타입을 필터링하는 것입니다:
type Diff<T, U> = T extends U ? never : T; type NonNullable<T> = Diff<T, null | undefined>;
3. Mapped Types
Mapped types는 속성을 변환하여 기존 타입을 기반으로 새로운 타입을 생성할 수 있게 해줍니다.
type ReadOnly<T> = { readonly [P in keyof T]: T[P]; }; interface User { name: string; age: number; } type ReadOnlyUser = ReadOnly<User>;
수정자를 추가하거나 제거할 수도 있습니다:
type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
4. Template Literal Types
TypeScript 4.1에서 도입된 이 기능은 문자열 타입을 직접 조작할 수 있게 해줍니다.
type World = 'world'; type Greeting = `hello ${World}`; // "hello world" type Color = 'red' | 'blue'; type Quantity = 'light' | 'dark'; type Palette = `${Quantity}-${Color}`; // "light-red" | "light-blue" | "dark-red" | "dark-blue"
CSS 클래스나 이벤트 이름과 같이 특정 패턴을 따르는 문자열을 타이핑하는 데 매우 유용합니다.
5. infer 키워드
조건부 타입 내의 infer 키워드는 다른 타입에서 타입을 추출할 수 있게 해줍니다.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function check(): boolean { return true; } type CheckReturn = ReturnType<typeof check>; // boolean
여기서 우리는 TypeScript에게 함수의 반환 타입 R을 "추론"하고 반환하도록 요청합니다.
결론
이러한 패턴을 마스터하면 견고하고 우수한 개발자 경험(DX)을 제공하는 라이브러리와 유틸리티를 작성할 수 있습니다. 고급 TypeScript의 목표는 복잡성을 위한 복잡성이 아니라, 안전성과 표현력입니다.