
TypeScript의 강력한 타입 시스템을 이해하고 활용하기 위한 첫 걸음으로, Day 1에서는 keyof, typeof, Record, Omit, Exclude, Extract, 조건부 타입, infer, 템플릿 리터럴 타입 등 실무에서도 매우 자주 사용하는 핵심 문법들을 학습했습니다.
keyof T는 객체 타입 T의 모든 키를 유니언 타입으로 반환합니다. 예를 들어 keyof { name: string; age: number }는 "name" | "age"가 됩니다. 또한 제네릭을 사용할 때 T extends object처럼 타입의 범위를 제한해주면 타입 오류를 방지할 수 있습니다.
function getValue<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
keyof는 Record, Pick, Omit 등 다른 유틸리티 타입들과 자주 결합됩니다.keyof의 사용 대상을 명확히 하여 예기치 않은 타입 추론을 방지할 수 있습니다.Record<K, V>는 K를 키로, V를 값으로 갖는 객체 타입을 생성합니다.
type Role = "admin" | "user";
type RolePermissions = Record<Role, string[]>;
Omit<T, K>는 객체 타입 T에서 K에 해당하는 속성을 제거한 타입을 생성합니다.
type User = { id: number; password: string };
type PublicUser = Omit<User, "password">; // { id: number }
Pick<T, Exclude<keyof T, K>>처럼 동작합니다.Exclude<T, U>는 유니언 타입 T에서 U에 해당하는 타입을 제외합니다.
type Role = "admin" | "user" | "guest";
type PublicRole = Exclude<Role, "admin">; // "user" | "guest"
Extract<T, U>는 유니언 타입 T에서 U에 해당하는 타입만 추출합니다.
type Role = "admin" | "user" | "guest";
type Staff = Extract<Role, "admin" | "user">; // "admin" | "user"
Exclude와 반대입니다.조건부 타입은 A extends B ? X : Y 형태로 타입 간 관계에 따라 결과를 분기시킵니다. infer 키워드를 사용하면 타입을 추론할 수 있습니다.
type ElementType<T> = T extends Array<infer U> ? U : T;
type A = ElementType<string[]>; // string
type B = ElementType<boolean>; // boolean
infer는 ReturnType, Parameters 등 여러 내장 유틸리티 타입에서도 사용됩니다.템플릿 리터럴 타입은 문자열 리터럴과 유니언 타입을 결합해 새로운 문자열 타입 조합을 생성할 수 있습니다.
type Size = "sm" | "md";
type Variant = "primary" | "secondary";
type ButtonClass = `btn-${Size}-${Variant}`;
이번 학습을 통해 타입스크립트의 핵심 유틸리티 타입들과 그 동작 원리에 대해 이해하게 되었습니다. 단순히 문법을 외우는 것을 넘어, 타입 설계에 있어 어떤 유틸리티를 언제 사용해야 하는지에 대한 감각을 익힐 수 있었습니다.
특히 객체 타입과 유니언 타입을 다룰 때 사용하는 Pick, Omit, Extract, Exclude의 차이와 활용 방식, 조건부 타입과 infer를 통해 타입을 유연하게 조작하는 방법, 템플릿 리터럴 타입을 통한 문자열 패턴 제한은 앞으로 타입 안정성을 높이는 데 큰 역할을 할 것입니다.
타입은 문법이 아니라 설계입니다. 그리고 설계는 연습과 시행착오를 통해 단단해집니다.