satisfies 연산자 — 타입 검증과 추론을 동시에
satisfies는 타입 검증은 하되, 추론된 타입 정보는 유지 하는 연산자입니다. TypeScript 4.9에서 도입되었습니다.
문제: as vs 타입 선언의 딜레마
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로 해결
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 비교
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' (리터럴 타입)
| 타입 검증 | 추론 유지 | 초과 속성 검사 | |
|---|---|---|---|
: Type | O | X (넓은 타입) | O |
as Type | X | X | X |
satisfies Type | O | O (좁은 타입) | O |
실전 활용 패턴
라우트 설정
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의 타입이 '/'로 좁혀짐
테마 색상 정의
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' (리터럴 타입 유지)
상수 맵에서의 활용
type StatusMessage = Record<number, string>;
const messages = {
200: '성공',
404: '찾을 수 없음',
500: '서버 오류',
} satisfies StatusMessage;
// messages[200]: '성공' (리터럴 타입)
// 존재하지 않는 키는 에러: messages[999] ← Error
as const와의 차이
// 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 이상에서 사용 가능하다
댓글 로딩 중...