satisfies타입 검증은 하되, 추론된 타입 정보는 유지 하는 연산자입니다. TypeScript 4.9에서 도입되었습니다.

문제: as vs 타입 선언의 딜레마

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

// 방법 1: 타입 선언 — 추론 정보 손실
const colors1: ColorMap = {
  red: '#ff0000',
  green: [0, 255, 0],
};
// colors1.red의 타입: string | number[] — 어떤 건지 모름
// colors1.red.toUpperCase(); // ❌ Error

// 방법 2: as — 검증 없음
const colors2 = {
  red: '#ff0000',
  green: [0, 255, 0],
} as ColorMap;
// 타입 오류를 잡아주지 않음

satisfies로 해결

TYPESCRIPT
const colors = {
  red: '#ff0000',
  green: [0, 255, 0],
} satisfies ColorMap;

// ✅ 타입 검증됨: ColorMap을 만족하는지 체크
// ✅ 추론 유지됨: red는 string, green은 number[]
colors.red.toUpperCase(); // OK — string으로 추론
colors.green.map((v) => v * 2); // OK — number[]로 추론

as, 타입 선언, satisfies 비교

TYPESCRIPT
type Config = {
  port: number;
  host: string;
  debug?: boolean;
};

// 타입 선언 (:) — 검증 O, 추론 △ (넓은 타입)
const a: Config = { port: 3000, host: 'localhost' };
// a.port: number

// as — 검증 X, 추론 X
const b = { port: 3000, host: 'localhost' } as Config;
// 잘못된 속성도 에러 없음

// satisfies — 검증 O, 추론 O (좁은 타입)
const c = { port: 3000, host: 'localhost' } satisfies Config;
// c.port: 3000 (리터럴 타입)
// c.host: 'localhost' (리터럴 타입)
타입 검증추론 유지초과 속성 검사
: TypeOX (넓은 타입)O
as TypeXXX
satisfies TypeOO (좁은 타입)O

실전 활용 패턴

라우트 설정

TYPESCRIPT
type Route = {
  path: string;
  component: string;
  auth?: boolean;
};

type Routes = Record<string, Route>;

const routes = {
  home: { path: '/', component: 'HomePage' },
  about: { path: '/about', component: 'AboutPage' },
  admin: { path: '/admin', component: 'AdminPage', auth: true },
} satisfies Routes;

// 키가 정확히 추론됨
type RouteKeys = keyof typeof routes; // 'home' | 'about' | 'admin'

// routes.home.path의 타입이 '/'로 좁혀짐

테마 색상 정의

TYPESCRIPT
type ThemeColor = `#${string}` | `rgb(${string})`;
type Theme = Record<string, ThemeColor>;

const theme = {
  primary: '#3b82f6',
  secondary: '#64748b',
  danger: 'rgb(239, 68, 68)',
  // invalid: 'blue', // ❌ Error — ThemeColor 패턴이 아님
} satisfies Theme;

// theme.primary: '#3b82f6' (리터럴 타입 유지)

상수 맵에서의 활용

TYPESCRIPT
type StatusMessage = Record<number, string>;

const messages = {
  200: '성공',
  404: '찾을 수 없음',
  500: '서버 오류',
} satisfies StatusMessage;

// messages[200]: '성공' (리터럴 타입)
// 존재하지 않는 키는 에러: messages[999] ← Error

as const와의 차이

TYPESCRIPT
// as const — 타입 검증 없이 리터럴로 고정
const config1 = {
  port: 3000,
  host: 'localhost',
  typo: true,  // 오타인데 에러 안 남
} as const;

// satisfies — 타입 검증 + 리터럴 추론
const config2 = {
  port: 3000,
  host: 'localhost',
  // typo: true,  // ❌ Error (Config에 typo가 없음)
} satisfies Config;

// 둘 다 쓰면: 타입 검증 + 완전한 리터럴 타입
const config3 = {
  port: 3000,
  host: 'localhost',
} as const satisfies Config;
// config3.port: 3000 (숫자 리터럴)

공부하다 보니 as const satisfies Type 조합이 가장 강력하더라고요. 타입 검증도 되고, 리터럴 타입도 완전히 보존됩니다.

정리

  • satisfies는 타입 검증과 추론 유지를 동시에 달성한다
  • 타입 선언(:)은 추론을 넓히고, satisfies는 추론을 보존한다
  • as const satisfies Type 조합이 가장 강력한 패턴이다
  • 상수 맵, 라우트 설정, 테마 정의 등에서 특히 유용하다
  • TypeScript 4.9 이상에서 사용 가능하다
댓글 로딩 중...