ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TypeScript에서 Record 타입 선언 vs satisfies의 차이
    TypeScript 2026. 2. 21. 21:48

    TypeScript를 쓰다 보면 객체의 키와 값을 정확하게 제한하고 싶을 때가 많다.

     

    예를 들어 이런 유니온 타입이 있다고 해보자.

    type Zombie = "dead" | "alive";

     

    그리고 이 Zombie 상태에 대해 boolean 값을 매핑하고 싶다.

    이때 흔히 떠올리는 방법은 Record를 쓰는 것이다.

     

    방법 1 -> 타입을 직접 지정하는 방식

    const obj: Record<Zombie, boolean> = {
      dead: false,
      alive: true,
    };

    깔끔해 보이고, 얼핏 보면 문제 없어 보인다.

    • dead, alive 외의 키는 못 들어가고
    • 값도 boolean으로 제한된다

    그런데 여기에는 은근히 중요한 단점이 숨어 있다.

    satisfies를 보고나면 알 수 있다.

     

    방법  2 -> as const satisfies를 사용하는 방식

    const obj2 = {
      dead: false,
      alive: true,
    } as const satisfies Record<Zombie, boolean>;

    이 코드는 조금 낯설어 보일 수 있다.

    하지만 최근 TypeScript에서 권장되는 패턴 중 하나다.

     

    겉보기엔 같은데, 뭐가 다를까? 🤔

    핵심 차이 한 줄 요약

    타입을 “덮어씌우느냐” vs “검증만 하느냐”의 차이

    이 차이는 타입 추론 결과에서 바로 드러난다.

     

    🍪 객체의 타입 추론 결과

    Record로 직접 지정한 경우

    obj.dead; // boolean

    타입스크립트 입장에서는

    const obj: Record<Zombie, boolean>

    → “아, 그냥 boolean이네”

    즉, 리터럴 정보가 모두 사라진다.

     

    as const satisfies를 쓴 경우

    obj2.dead; // false
    obj2.alive; // true

    이 경우 타입은 이렇게 추론된다.

    {
      readonly dead: false;
      readonly alive: true;
    }
    

    ✔ 값이 리터럴 타입 그대로 유지된다
    ✔ readonly도 자동으로 붙는다

     

    🧇 추가 키에 대한 처리

    Record 방식

    const obj: Record<Zombie, boolean> = {
      dead: false,
      alive: true,
      unknown: true, // ❌ 에러
    };

    당연히 에러가 난다.
    이건 satisfies도 동일하다.

     

    하지만 이런 경우는?

    const createMap = <T extends Record<Zombie, boolean>>(map: T) => map;
    
    const result = createMap({
      dead: false,
      alive: true,
      // 여기서 타입 추론이 흐릿해질 수 있음
    });

    이런 제네릭 상황에서는
    satisfies가 훨씬 예상 가능한 타입 추론을 제공한다.

     

    실무에서 체감되는 차이점 😵‍💫

    Record 타입 선언의 특징

    • 객체 타입을 강제로 고정
    • 리터럴 타입 정보 손실
    • 이후 코드에서 추론이 단순해짐 (좋을 수도, 나쁠 수도)

    as const satisfies의 특징

    • 객체의 실제 형태를 유지
    • 타입 안정성은 그대로 확보
    • 에러 메시지가 더 친절
    • enum-like 객체, 설정 객체에 특히 강력

     

    언제 어떤 걸 써야 할까? 🧭

    이런 경우엔 Record도 충분

    • 객체를 단순한 맵으로만 사용할 때
    • 리터럴 타입이 중요하지 않을 때
    • 타입을 명시적으로 고정하고 싶을 때

    이런 경우엔 satisfies가 훨씬 낫다

    • 설정 객체 / 상태 매핑
    • enum 대용 객체
    • 값 자체가 의미를 가지는 경우
    • “타입 검증만 하고, 추론은 맡기고 싶을 때”

    예를 들면

    const zombieLabel = {
      dead: "사망",
      alive: "생존",
    } as const satisfies Record<Zombie, string>;

    이 패턴은 요즘 TS 코드에서 정말 자주 보인다.

     

    특히, 위와 같은 경우에서는

    Record의 경우 zombieLabel.dead의 타입이 string으로 잡히고,

    satisfies의 경우 zombieLabel.dead의 타입이 "사망"으로 잡히게 된다.

     

    정리해보면 🧠

    • Record<Zombie, boolean>
      → 타입을 지정한다
    • as const satisfies Record<Zombie, boolean>
      → 타입을 검증한다

    그리고 실무에서는 대부분

    “지정”보다는 “검증”이 더 안전하다

    는 쪽으로 흐르고 있다.

     

    한 줄 요약

    타입스크립트에서 객체 타입을 고정하고 싶으면 단순 Record 지정
    타입 안정성만 보장하고 싶으면 satisfies

     

    참고자료 및 링크 📎

Designed by Tistory.