TypeScript satisfies 연산자, 언제 써야 할까

약 1분

TypeScript 4.9에서 추가된 satisfies 연산자는 “이 값이 어떤 타입을 만족하는지 검사는 하되, 추론된 구체적인 타입은 유지하고 싶다” 라는 요구를 해결합니다.

문제: 타입 주석을 쓰면 정보가 사라진다

type Theme = Record<string, string | number[]>;

const theme: Theme = {
  primary: '#2563eb',
  spacing: [4, 8, 16],
};

theme.spacing.map((v) => v * 2);
// ❌ 'string | number[]' 형식에 'map' 속성이 없습니다.

theme을 Theme으로 선언하는 순간 각 속성이 string | number[]로 넓어집니다. 분명 spacing은 배열인데, 쓸 때마다 좁혀 줘야 합니다.

문제: as 단언은 검사를 건너뛴다

const theme = {
  primary: '#2563eb',
  spacing: [4, 8, 16],
  // 오타가 있어도 통과됨
  radius: undefined,
} as Theme;

as는 “믿어 달라”는 뜻이라, 실제로 타입에 맞지 않는 값이 들어가도 잡아주지 못합니다.

해결: satisfies

const theme = {
  primary: '#2563eb',
  spacing: [4, 8, 16],
} satisfies Theme;

theme.spacing.map((v) => v * 2); // ✅ number[]로 추론됨
theme.primary.toUpperCase();     // ✅ string으로 추론됨
  • Theme에 맞지 않으면 컴파일 에러가 납니다.
  • 그러면서 theme의 타입은 리터럴 그대로 { primary: string; spacing: number[] }로 유지됩니다.

실전 활용 예

1. 설정 객체

const routes = {
  home: '/',
  post: (slug: string) => `/posts/${slug}`,
} satisfies Record<string, string | ((...args: never[]) => string)>;

routes.post('hello'); // ✅ 함수로 호출 가능

2. 유니온 키를 빠짐없이 채우기

type Status = 'idle' | 'loading' | 'error';

const label = {
  idle: '대기',
  loading: '불러오는 중',
  error: '오류',
} satisfies Record<Status, string>;
// Status에 값이 추가되면 여기서 바로 에러가 나서 누락을 막아 줍니다.

3. 상수 배열과 as const 조합

const TAGS = ['Astro', 'SEO', 'TypeScript'] as const satisfies readonly string[];
type Tag = (typeof TAGS)[number]; // 'Astro' | 'SEO' | 'TypeScript'

정리

방법 타입 검사 추론 정보 유지
const x: T = ... ✅ ❌ (T로 넓어짐)
... as T ❌ (단언) ❌
... satisfies T ✅ ✅

“형태는 검사하고 싶지만 값의 구체적인 타입은 그대로 쓰고 싶다”면 satisfies가 정답입니다.

댓글