TypeScript 4.5 const assertion과 타입 추론 개선
배경
레거시 프로젝트를 TS 4.3에서 4.5로 업그레이드하는 작업을 진행했다. 릴리즈 노트를 보다가 const assertion 관련 개선사항이 눈에 띄어 실무에 적용해봤다.
기존 문제
상수 객체를 정의할 때 매번 타입을 명시적으로 선언해야 했다.
type Route = {
path: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
};
const routes: Route[] = [
{ path: '/users', method: 'GET' },
{ path: '/users', method: 'POST' }
];
이 방식은 타입 안정성은 있지만 자동완성이나 정확한 리터럴 타입을 활용하기 어려웠다.
const assertion 활용
const routes = [
{ path: '/users', method: 'GET' },
{ path: '/users', method: 'POST' }
] as const;
type RouteMethod = typeof routes[number]['method']; // 'GET' | 'POST'
type RoutePath = typeof routes[number]['path']; // '/users'
as const를 사용하면 readonly 속성과 함께 정확한 리터럴 타입이 추론된다.
실전 적용
API 엔드포인트 설정에 적용했다.
export const API_ENDPOINTS = {
users: '/api/users',
posts: '/api/posts',
comments: '/api/comments'
} as const;
type Endpoint = typeof API_ENDPOINTS[keyof typeof API_ENDPOINTS];
function fetchData(endpoint: Endpoint) {
// 타입 체크와 자동완성 모두 동작
return fetch(endpoint);
}
fetchData(API_ENDPOINTS.users); // OK
fetchData('/api/unknown'); // Error
주의사항
readonly 속성 때문에 런타임에 수정이 불가능하다. 변경 가능한 데이터에는 사용하지 않는 것이 좋다.
const config = { port: 3000 } as const;
config.port = 4000; // Error: Cannot assign to 'port'
설정값이나 상수 정의에는 유용하지만, 동적으로 변경되는 상태에는 부적합하다.
결론
const assertion은 타입 선언 없이도 정확한 타입 추론을 가능하게 한다. 특히 설정 객체나 API 엔드포인트처럼 변경되지 않는 값들을 다룰 때 유용했다.