TypeScript 5.5 satisfies 연산자와 타입 좁히기 패턴
문제 상황
라우팅 설정 객체를 만들면서 타입 안전성을 확보하는 과정에서 기존 방식의 한계에 부딪혔다. as const를 사용하면 리터럴 타입은 보존되지만 구조 검증이 없고, 타입 어노테이션을 쓰면 리터럴 타입이 넓어지는 문제가 있었다.
const routes = {
home: '/home',
user: '/user/:id',
admin: '/admin'
} as const;
type Route = typeof routes[keyof typeof routes];
// '/home' | '/user/:id' | '/admin'
satisfies 연산자
TypeScript 5.0에서 도입된 satisfies는 타입 체크는 하되 타입 추론은 유지한다. 이를 활용해 구조 검증과 리터럴 타입 보존을 동시에 달성했다.
type RouteConfig = Record<string, `/${string}`>;
const routes = {
home: '/home',
user: '/user/:id',
admin: '/admin'
} as const satisfies RouteConfig;
// 타입 에러 발생
const invalid = {
home: 'home' // '/' 없음
} as const satisfies RouteConfig;
실전 활용
API 엔드포인트 설정에서도 유용했다. 메서드와 경로를 함께 관리하면서 타입 안전성을 확보했다.
type ApiEndpoint = {
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
path: `/${string}`;
};
const endpoints = {
getUser: { method: 'GET', path: '/users/:id' },
createUser: { method: 'POST', path: '/users' }
} as const satisfies Record<string, ApiEndpoint>;
type EndpointKey = keyof typeof endpoints;
type EndpointPath = typeof endpoints[EndpointKey]['path'];
결론
satisfies를 사용하면서 설정 객체의 타입 안전성이 크게 개선되었다. 컴파일 타임에 오류를 잡을 수 있고, IDE 자동완성도 정확해졌다. 기존 프로젝트에 점진적으로 적용 중이다.