TypeScript는 정적 타입 시스템을 통해 런타임 오류를 줄이고, 코드의 안정성과 유지보수성을 높이는 데 큰 역할을 한다. 특히 내장된 타입 유틸리티(Utility Types)는 기존 타입을 변형하거나 새로운 타입을 생성하는 데 매우 유용하다. 이 글에서는 TypeScript의 핵심 타입 유틸리티와 문자열 조작용 타입 도구들을 깊이 있게 살펴본다.
1. Awaited<T>: Promise 중첩 해제
비동기 작업에서 최종적으로 반환되는 값을 추출할 때 사용된다. 중첩된 Promise도 재귀적으로 처리한다.
type A = Awaited<Promise<string>>; // string
type B = Awaited<Promise<Promise<number>>>; // number
type C = Awaited<boolean | Promise<number>>; // boolean | number
구현은 조건부 타입과 infer를 활용해 then 메서드의 콜백 인자를 추론한다.
2. ConstructorParameters<T>: 생성자 파라미터 추출
클래스 또는 생성자 함수의 인자 타입을 튜플로 반환한다.
class Point {
constructor(public x: number, public y: number) {}
}
type PointArgs = ConstructorParameters<typeof Point>; // [x: number, y: number]
내장 생성자에도 적용 가능하다:
type ErrorArgs = ConstructorParameters<ErrorConstructor>; // [message?: string]
3. Exclude<T, U>: 유니온에서 특정 타입 제거
T에서 U에 할당 가능한 모든 타입을 제거한다.
type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
type T2 = Exclude<string | number | null, null>; // string | number
조건부 타입을 기반으로 구현되며, never는 유니온에서 자동 제거된다.
4. Extract<T, U>: 유니온에서 특정 타입 추출
T에서 U에 할당 가능한 타입만 남긴다.
type T1 = Extract<'a' | 'b' | 'c', 'a' | 'd'>; // 'a'
type T2 = Extract<Function | string, Function>; // Function
5. InstanceType<T>: 인스턴스 타입 추출
생성자 함수가 반환하는 인스턴스의 타입을 얻는다.
class User {
name = '';
}
type UserInstance = InstanceType<typeof User>; // User
type RegExpInstance = InstanceType<RegExpConstructor>; // RegExp
6. NonNullable<T>: null/undefined 제거
유니온 타입에서 null과 undefined를 제거한다.
type T = NonNullable<string | null | undefined>; // string
실제 구현은 T & {}로, 객체 타입이 아닌 값은 필터링된다.
7. Omit<T, K>: 객체에서 속성 제거
객체 타입에서 지정된 키를 제외한 나머지 속성만 남긴다.
interface Config {
host: string;
port: number;
timeout?: number;
}
type BasicConfig = Omit<Config, 'timeout'>; // { host: string; port: number; }
Pick과 Exclude를 조합해 구현된다.
8. OmitThisParameter<T>: 함수의 this 파라미터 제거
함수 타입에서 명시적 this 매개변수를 제거한다.
function greet(this: { name: string }) {
return `Hello, ${this.name}`;
}
type PlainGreet = OmitThisParameter<typeof greet>; // () => string
9. Parameters<T>: 함수 파라미터 추출
함수의 인자 타입을 튜플로 반환한다.
declare function fetchData(url: string, options: RequestInit): Promise<any>;
type FetchArgs = Parameters<typeof fetchData>; // [url: string, options: RequestInit]
10. Partial<T>: 모든 속성을 선택적(optional)으로
객체의 모든 속성을 ? 로 감싸 선택적으로 만든다.
interface UserForm {
email: string;
password: string;
}
type PartialForm = Partial<UserForm>; // { email?: string; password?: string; }
11. Pick<T, K>: 객체에서 특정 속성만 선택
지정된 키만 포함하는 새 객체 타입을 생성한다.
type NameOnly = Pick<UserForm, 'email'>; // { email: string }
12. Readonly<T>: 모든 속성을 읽기 전용으로
객체의 모든 속성에 readonly를 적용한다.
type ImmutableUser = Readonly<{ id: number; name: string }>;
// { readonly id: number; readonly name: string; }
반대로 수정 가능하게 만들려면 다음과 같이 사용할 수 있다:
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
13. Record<K, T>: 키-값 매핑 객체 생성
지정된 키 집합과 값 타입으로 객체 타입을 정의한다.
type HttpStatus = Record<200 | 404 | 500, string>;
// { 200: string; 404: string; 500: string; }
14. Required<T>: 모든 속성을 필수로
Partial의 반대 개념으로, 선택적 속성을 모두 필수로 만든다.
interface Options {
debug?: boolean;
retries: number;
}
type StrictOptions = Required<Options>; // { debug: boolean; retries: number; }
-? 문법을 사용해 옵셔널 마커를 제거한다.
15. ReadonlyArray<T>: 읽기 전용 배열
배열 요소 수정이나 변경 메서드 사용을 금지한다.
const items: ReadonlyArray<string> = ['a', 'b'];
// items.push('c'); // ❌ 오류
16. ReturnType<T>: 함수 반환 타입 추출
함수의 반환 타입을 추론한다.
function createId(): string {
return crypto.randomUUID();
}
type IdType = ReturnType<typeof createId>; // string
17. ThisParameterType<T>: 함수의 this 타입 추출
함수 시그니처에서 명시된 this 타입을 반환한다.
function log(this: Console, msg: string) {
this.log(msg);
}
type ThisTypeOfLog = ThisParameterType<typeof log>; // Console
18. ThisType<T>: this 타입 힌트 제공
타입 체크를 위해 this의 예상 타입을 명시적으로 지정한다. noImplicitThis 옵션이 활성화되어야 효과적이다.
interface ApiContext {
baseUrl: string;
fetch: (path: string) => Promise<any>;
}
const apiMethods: ThisType<ApiContext> & { [key: string]: Function } = {
getUser(id: string) {
return this.fetch(`/users/${id}`); // ✅ this는 ApiContext 타입으로 간주
}
};
문자열 조작용 타입 유틸리티
TypeScript 4.1+부터 제공되는 문자열 리터럴 타입 변환 도구는 템플릿 리터럴 타입과 함께 강력한 타입 계산을 가능하게 한다.
Uppercase<S>: 모든 문자 대문자 변환
type T = Uppercase<'hello'>; // "HELLO"Lowercase<S>: 모든 문자 소문자 변환
type T = Lowercase<'HELLO'>; // "hello"Capitalize<S>: 첫 글자만 대문자
type T = Capitalize<'hello'>; // "Hello"Uncapitalize<S>: 첫 글자만 소문자
type T = Uncapitalize<'HELLO'>; // "hELLO"
이러한 도구들은 API 경로 생성, 이벤트 핸들러 이름 규칙 정의 등 다양한 메타프로그래밍 시나리오에서 유용하게 사용된다.