TypeScript 4.9의 satisfies 연산자로 타입 안정성 높이기
배경
프로젝트에서 라우트 설정 객체를 관리하는데, 기존 방식은 타입 안정성과 추론 사이에서 trade-off가 있었다. as const를 쓰면 추론은 좋지만 타입 체크가 약하고, 타입 어노테이션을 명시하면 리터럴 타입이 넓어지는 문제가 있었다.
기존 방식의 문제
type Route = {
path: string;
methods: string[];
};
// 방법 1: 타입 체크는 되지만 추론이 약함
const routes: Route = {
path: '/api/users',
methods: ['GET', 'POST'] // string[]로 추론됨
};
// 방법 2: 추론은 좋지만 오타 체크 안됨
const routes = {
path: '/api/users',
methds: ['GET', 'POST'] // 오타인데 에러 없음
} as const;
satisfies 연산자
TypeScript 4.9에서 추가된 satisfies 연산자로 두 마리 토끼를 잡았다.
const routes = {
path: '/api/users',
methods: ['GET', 'POST']
} as const satisfies Route;
// 장점 1: methods가 readonly ['GET', 'POST']로 좁게 추론
type Methods = typeof routes.methods[number]; // 'GET' | 'POST'
// 장점 2: 오타나 잘못된 필드는 컴파일 에러
const invalid = {
ppath: '/api/users', // Error: Object literal may only specify known properties
methods: ['GET']
} as const satisfies Route;
실무 적용
API 엔드포인트 설정에 적용하니 자동완성이 정확해지고, 런타임 오류가 줄었다.
const API_ROUTES = {
users: { path: '/api/users', methods: ['GET', 'POST'] },
posts: { path: '/api/posts', methods: ['GET', 'PUT', 'DELETE'] }
} as const satisfies Record<string, Route>;
// 타입 안전하게 사용 가능
function request(route: typeof API_ROUTES[keyof typeof API_ROUTES]) {
// route.methods는 정확한 리터럴 타입
}
결론
satisfies는 타입 체크와 타입 추론을 모두 살릴 수 있는 좋은 도구다. 특히 설정 객체나 상수 정의에 유용했다. TypeScript 4.9 이상이라면 적극 활용할 만하다.