TypeScript 4.1의 Template Literal Types로 타입 안정성 높이기
배경
프로젝트에서 REST API 엔드포인트를 문자열로 관리하다 보니 오타로 인한 런타임 에러가 종종 발생했다. /api/users/detail을 /api/user/detail로 잘못 입력하는 식의 실수였다.
TypeScript 4.1이 11월에 릴리즈되면서 Template Literal Types가 추가됐다는 소식을 듣고 바로 적용해봤다.
Template Literal Types 적용
기존에는 문자열 리터럴 유니온으로만 관리했다.
type Endpoint = '/api/users' | '/api/posts' | '/api/comments';
이제는 패턴을 조합할 수 있다.
type Resource = 'users' | 'posts' | 'comments';
type Action = 'list' | 'detail' | 'create';
type Endpoint = `/api/${Resource}/${Action}`;
// '/api/users/list' | '/api/users/detail' | ... (9개 조합)
실제 적용 사례
API 클라이언트에 적용했다.
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
interface ApiConfig {
baseURL: string;
}
class ApiClient {
constructor(private config: ApiConfig) {}
request<T>(method: HTTPMethod, endpoint: Endpoint): Promise<T> {
return fetch(`${this.config.baseURL}${endpoint}`, {
method,
}).then(res => res.json());
}
}
// 사용
const client = new ApiClient({ baseURL: 'https://api.example.com' });
client.request('GET', '/api/users/list'); // ✓
client.request('GET', '/api/user/list'); // ✗ 컴파일 에러
이벤트 시스템에도 활용
이벤트 버스에도 적용해서 이벤트 타입 안정성을 확보했다.
type EventType = 'user' | 'post';
type EventAction = 'created' | 'updated' | 'deleted';
type Event = `${EventType}:${EventAction}`;
class EventBus {
on(event: Event, handler: Function) {
// ...
}
}
const bus = new EventBus();
bus.on('user:created', () => {}); // ✓
bus.on('user:create', () => {}); // ✗ 컴파일 에러
한계
타입 조합이 많아지면 컴파일 시간이 길어진다. Resource 5개 × Action 5개면 25개 조합인데, 여기에 버전까지 추가하면 금방 늘어난다. 적절한 선에서 사용해야 한다.
정리
Template Literal Types 덕분에 문자열 기반 API를 타입 안전하게 관리할 수 있게 됐다. 기존 코드베이스에 점진적으로 적용 중이며, 특히 API 클라이언트와 이벤트 시스템에서 효과가 좋았다.