본문으로 건너뛰기
개발 머꼬
개발 노트JavaScript
hohyeon.dev21

as const로는 부족했던 상황에서 satisfies 연산자가 해결해준 것

  • #Engineering Note
  • #Type Safety
  • #TypeScript

문제 발생

설정 객체를 만들면서 정해진 타입(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"라고만 압니다 — 실제 객체에 developmentproduction이라는 정해진 키만 있다는 정보가 사라집니다.
  • 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만으로는 잡지 못했던 문제입니다.

  1. "이 값이 특정 인터페이스를 만족해야 하는데, 그 값의 구체적인 형태(리터럴 타입, 키 목록)도 그대로 활용하고 싶다"는 상황이면 satisfies가 정확히 맞는 도구입니다 — 설정 객체, 라우트 정의 맵, 색상 팔레트 등 "정해진 키 집합 + 각 값의 타입 제약"이 함께 필요한 곳에서 특히 유용합니다.
  2. 단순히 타입을 강제로 우회하고 싶을 때 쓰는 as(타입 단언)와는 목적이 다릅니다 — as는 컴파일러의 판단을 개발자가 대신 책임지고 덮어쓰는 것이고, satisfies는 오히려 추가로 검사를 받으면서 정보는 잃지 않는 더 안전한 방향입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.