TypeScript 5.2 satisfies 연산자로 타입 안전성 높이기

문제 상황

프로젝트에서 설정 객체를 다루다가 타입 오류가 자주 발생했다. as const를 쓰면 너무 좁아지고, 인터페이스로만 타이핑하면 오타를 잡지 못했다.

const config = {
  development: { apiUrl: 'http://localhost:3000' },
  production: { apiUrl: 'https://api.example.com' },
  staging: { apiUrl: 'https://staging.api.example.com' }
};

// apiUrl 오타를 내도 에러가 안 남
config.development.apiUr; 

satisfies 연산자 도입

TypeScript 5.0부터 지원되는 satisfies를 사용하면 타입 체크는 하면서도 추론을 유지할 수 있다.

type Environment = 'development' | 'production' | 'staging';
type Config = Record<Environment, { apiUrl: string }>;

const config = {
  development: { apiUrl: 'http://localhost:3000' },
  production: { apiUrl: 'https://api.example.com' },
  staging: { apiUrl: 'https://staging.api.example.com' }
} satisfies Config;

// 이제 오타를 잡아준다
config.development.apiUrl; // OK
// config.development.apiUr; // Error

실전 활용 케이스

라우트 설정에도 적용했다. 기존에는 as const로 처리했는데, satisfies를 쓰니 타입 안전성과 자동완성이 모두 개선됐다.

type Route = {
  path: string;
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
};

const routes = {
  getUser: { path: '/users/:id', method: 'GET' },
  createUser: { path: '/users', method: 'POST' }
} satisfies Record<string, Route>;

// 타입 추론이 정확하게 된다
routes.getUser.method; // 'GET' (literal type)

마이그레이션 시 주의점

팀 전체가 TypeScript 5.0 이상을 사용해야 한다. CI/CD 파이프라인의 Node 버전도 확인이 필요했다. 우리는 Node 18 LTS를 쓰고 있어서 문제없었다.

as 단언을 무작정 satisfies로 바꾸면 안 된다. as는 강제 변환이고 satisfies는 검증이므로 용도가 다르다. 기존 코드에서 as를 남발했다면 satisfies로 바꿀 때 타입 에러가 많이 날 것이다. 그게 정상이다.