as const로는 부족했던 상황에서 satisfies 연산자가 해결해준 것
문제 발생
설정 객체를 만들면서 정해진 타입(Record<string, Config>)을 만족하는지 검사받고 싶었지만, 동시에 각 키에 접근했을 때 정확한 리터럴 타입(예: "development" | "production")도 유지하고 싶었습니다. 둘 중 하나만 가능해 보였습니다.
interface Config {
url: string;
retries: number;
}
const envs: Record<string, Config> = {
development: { url: "http://localhost", retries: 1 },
production: { url: "https://api.example.com", retries: 3 },
};
envs.development.url; // 타입이 string으로 넓어짐 — "development"라는 특정 키 접근이라는 정보를 잃음원인 분석
타입을 다루는 기존 두 방법은 각자 한계가 있었습니다.
- 명시적 타입 표기(
const envs: Record<string, Config> = {...}):Config조건을 만족하는지 검사는 해주지만, 변수의 타입이 곧바로Record<string, Config>로 넓혀집니다(widening).envs.development처럼 특정 키로 접근해도 TypeScript는 "어떤 문자열 키로 접근했든 결과는Config"라고만 압니다 — 실제 객체에development와production이라는 정해진 키만 있다는 정보가 사라집니다. as const: 값의 리터럴 타입을 그대로 보존해주지만,Config인터페이스를 만족하는지에 대한 검사 자체를 하지 않습니다 — 예를 들어retries에 실수로 문자열을 넣어도 잡아내지 못합니다.
해결 방안
TypeScript 4.9+의 satisfies 연산자는 이 둘을 동시에 만족시킵니다 — "이 값이 특정 타입 조건을 만족하는지 검사하되, 그 값의 원래(더 좁은) 타입은 그대로 유지한다"는 뜻입니다.
const envs = {
development: { url: "http://localhost", retries: 1 },
production: { url: "https://api.example.com", retries: 3 },
} satisfies Record<string, Config>;
envs.development.url; // 여전히 "development" 키로 접근한 것으로 정확히 추론됨
// envs.staging은 존재하지 않는 키이므로 접근 시 에러동시에 만약 retries: "three"처럼 잘못된 타입 값을 넣으면 satisfies 검사에서 즉시 컴파일 에러가 납니다 — as const만으로는 잡지 못했던 문제입니다.
- "이 값이 특정 인터페이스를 만족해야 하는데, 그 값의 구체적인 형태(리터럴 타입, 키 목록)도 그대로 활용하고 싶다"는 상황이면
satisfies가 정확히 맞는 도구입니다 — 설정 객체, 라우트 정의 맵, 색상 팔레트 등 "정해진 키 집합 + 각 값의 타입 제약"이 함께 필요한 곳에서 특히 유용합니다. - 단순히 타입을 강제로 우회하고 싶을 때 쓰는
as(타입 단언)와는 목적이 다릅니다 —as는 컴파일러의 판단을 개발자가 대신 책임지고 덮어쓰는 것이고,satisfies는 오히려 추가로 검사를 받으면서 정보는 잃지 않는 더 안전한 방향입니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.