
Intro
타입스크립트에서 satistfies 키워드를 사용해보신적 있으신가요?
왜 새로운 기능이 추가됐을까요? 먼저 아래 코드를 살펴보겠습니다.
interface Config {
mode: 'light'|'dark',
retry: number
}
const config: Config = {
mode: "dark",
retry: 3,
};
문제가 없어보이네요? 이런 가정을 해보면 어떨까요.
Config 타입을 만족하면서 실제 선언된 리터럴 타입이 추론됐으면 좋겠다
그러니까 config 변수 선언에서 할당된 mode:"dark", retry:3을 타입추론으로 얻고 싶습니다.
위 코드를 다시보니 선언 시점에 mode 필드가 dark라는게 정해졌는데 mode가 string으로 추론되네요. 타입이 넓어졌습니다. ("dark" -> string)
const config = {
mode: 'dark',
retry: 3,
} as const;

as const를 사용해서 이렇게 선언하면 되겠네요! 이제 리터럴 타입이 추론됩니다.
그런데 이번엔 Config 인터페이스를 만족하는지 알 수 없게 됐네요. Config의 명세가 변경되면 이 코드는 에러가 날 예정입니다.
Config명세에서 retry필드가 빠지거나 mode에서 유효값이 바뀌어도 컴포일 에러가 나지 않습니다.
satistfies는 이런 니즈를 위해 탄생했습니다!
satistfies는 어떤 상황을 해결하는가
const value = expression satisfies SomeType;
satistfies는 타입을 만족하는지 검증만 합니다.
다른 방식과 비교를 해보겠습니다.
const value:SomeType = expression;
위 코드는 "value는 SomeType이다."를 의미합니다. 이후 value는 SomType으로 추론됩니다.
const value = expression as SomeType;
위 코드는 "value가 SomeType이라고 주장(assertion)한다" 를 의미합니다.
이 경우 역시 value가 SomeType으로 추론됩니다. 차이점은 타입이 실제로 만족하는지 컴파일러가 검증하지 않고 개발자의 판단을 따라간다는 점입니다.
const value = expression satisfies SomeType;
이제 다시 satisfies를 보겠습니다. 이 코드는 value가 SomeType을 만족하는지 컴파일러가 비교하면서 expression의 타입으로 추론됩니다. 즉 타입 검증과 타입 추론이 격리됐네요. 이게 핵심입니다.
앞선 예제를 다시 돌아보겠습니다.
const config = {
mode: 'dark',
retry: 3,
} satisfies Config;
이제 이 코드는 Config 타입을 만족하면서 리터럴 타입으로 추론됩니다.
따라서 다음 처럼 추론됩니다.
config.mode; // 'dark'
config.retry; // 3
즉 타입 검증과 타입 추론을 격리하고 싶을 때 사용하시면 됩니다.
satistfies 사용 시나리오
유니온타입에 타입 매핑
interface FeatureOption {
enabled: boolean;
rollout: number;
}
type OptionsType = 'search' | 'ads';
const featureOptions = {
search: { enabled: true, rollout: 100 },
ads: { enabled: false, rollout: 0 },
} as const satisfies Record<OptionsType, FeatureOption>;
console.log(featureOptions.search.rollout); // 100
console.log(featureOptions.ads.rollout); // 0
꽤 자주 발생하는 상황입니다.
option의 유형도 정의(OptionsType)되어있고 옵션의 구조(FeatureOptions)도 정의되어 있습니다.
모든 options유형이 featureOptions에 정의되어야 하고 OptionType이 새롭게 추가되면 이 코드에서 새로운 옵션을 정의하기 위해 컴파일 에러가 나야 합니다. 예를 들어 OptionsType에 "newOptionsType"이 추가 됐다고 가정 해보겠습니다.
interface FeatureOption {
enabled: boolean;
rollout: number;
}
// ✅ 새로운 타입이 추가됐습니다.
type OptionsType = 'search' | 'ads' | 'newOptionType';
// 🔴 새로운 타입이 추가됐는데 featureOptions에 정의되지 않아 타입 에러가 발생합니다.
const featureOptions = {
search: { enabled: true, rollout: 100 },
ads: { enabled: false, rollout: 0 },
} as const satisfies Record<OptionsType, FeatureOption>;
// 🔴형식 '{ readonly search: { readonly enabled: true; readonly rollout: 100; }; readonly ads: { readonly enabled: false; readonly rollout: 0; }; }'이(가) 필요한 형식 'Record<OptionsType, FeatureOption>'을(를) 충족하지 않습니다.
// newOptionType' 속성이 '{ readonly search: { readonly enabled: true; readonly rollout: 100; }; readonly ads: { readonly enabled: false; readonly rollout: 0; }; }' 형식에 없지만 'Record<OptionsType, FeatureOption>' 형식에서 필수입니다.ts(1360)
이제 타입별 옵션 객체를 정의해야 하는 모든 코드에서 옵션타입이 변경될 떄 에러가 발생하겠네요! 런타입 에러를 걱정하지 않아도 되겠습니다.
할당된 값이 추론되어야 한다
interface FeatureOption {
enabled: boolean;
rollout: number;
}
type OptionsType = 'search' | 'ads';
const featureOptions = {
search: { enabled: true, rollout: 100 },
ads: { enabled: false, rollout: 0 },
} as const satisfies Record<OptionsType, FeatureOption>;
// 🟢 할당된 값이 각각 추론됨을 확인할 수 있습니다.
console.log(featureOptions.search.rollout); // 100
console.log(featureOptions.ads.rollout); // 0
앞서 서두에서 소개드렸던 상황입니다.
타입을 만족하면서 실제로 할당한 값들이 추론되네요. 이는 분기 로직에서 더욱 가치가 커집니다.
interface Action {
label: string;
requiresAuth: boolean;
}
const actions = {
save: { label: 'Save', requiresAuth: true },
delete: { label: 'Delete', requiresAuth: true },
preview: { label: 'Preview', requiresAuth: false },
} as const satisfies Record<string, Action>;
function handleAction(type: keyof typeof actions) {
const action = actions[type];
if (action.requiresAuth) {
// 🟢 Delete, Save케이스로 타입이 좁혀져서 추론됩니다
console.log(action);
}
}
타입이 확장될 수 있다
type Handler = (input: unknown) => {
success: boolean;
};
const handler = ((input) => {
return {
success: true,
timestamp: Date.now(),
};
}) satisfies Handler;
다음은 satisfies의 유연함을 이해할 수 있는 상황입니다. type Handler에서는 timestamp가 정의되지 않았습니다.
success boolean만 만족하는지만 검증하면서 정의한 코드가 그대로 추론됩니다.

이미지 처럼 handler변수의 타입 추론에서 timestamp가 포함되어 있습니다.
마무리
다시 한 번 정리하자면 satisfies은 타입 정의와 타입 추론을 격리합니다. 이를 바탕으로 실제 선언된 타입을 구체적으로 추론하여 타입 좁히기가 가능해졌습니다. satisfies 문법이 추가되기 전에 타입을 역으로 넓히거나 코드에 암묵적 전제가 깔려있었을지 모릅니다.
satisfies로 더 구체적인 타입을 정의해보시길 바랍니다.
'typescript' 카테고리의 다른 글
| 변수처럼 사용하는 제네릭 타입 (0) | 2025.12.29 |
|---|---|
| 브랜드 타입(Brand Type)을 이용한 타입 좁히기 (0) | 2025.12.23 |
| 객체지향 설계: 자율적인 책임의 힘 (0) | 2025.08.27 |
| 타입스크립트의 공변성과 반변성 (2) | 2024.07.13 |
| any 타입이 유용한 경우 (1) | 2024.07.13 |