Javascript

TypeScript로 타입을 강화하는 방법: satisfies와 Uppercase<T> 활용하기

드리프트2 2024. 8. 24. 10:30

 

 

 

TypeScript 4.9에 새롭게 도입된 satisfies 연산자, 여러분은 사용해보셨나요?

 

저는 이 기능을 굉장히 좋아합니다.

 

satisfies가 도입된 지 1년 반 정도가 지난 지금, 초기에 예상했던 것보다 훨씬 다양한 곳에서 이 연산자가 유용하다는 것을 체감하게 됩니다.

 

그래서 오늘은 이 기능을 활용한 몇 가지 유용한 예시를 소개하려고 합니다.

 

satisfies의 기본 개념

이번 글에서는 satisfies의 기초적인 설명은 생략하고, 실용적인 활용 예시를 중점적으로 다루겠습니다.

 

더 깊이 있는 설명이 필요하신 분들은 아래 블로그 글을 참고 하시면 됩니다.

 

https://mycodings.fly.dev/blog/2023-07-14-understanding-typescript-satisfies-operator

 

타입스크립트 satisfies 연산자(operator) 이해하기

타입스크립트 satisfies 연산자(operator) 이해하기

mycodings.fly.dev

 

Uppercase와 함께 타입 강화하기

이번에 소개할 방법은 satisfiesUppercase를 함께 사용하여 기존 타입을 보다 강력하게 만드는 것입니다.

 

예를 들어, COLOR 객체의 키를 대문자로 강제하고 싶을 때가 있을 것입니다.

 

이를 타입으로 정의하면 다음과 같이 표현할 수 있습니다.

 

type ColorKey = Uppercase<string>

 

Uppercase<T>는 문자열 타입을 받아 그 문자열을 대문자로 변환한 타입을 반환합니다.

 

이와 유사한 기능을 하는 다른 함수들도 존재합니다.

 

ColorKey 타입을 활용하여 "키가 대문자로 제한된 COLOR 객체"를 만들 수 있습니다. 두 가지 방법을 살펴보겠습니다.

// 타입 주석 사용
const COLOR_ANNOTATE: Record<ColorKey, string> = {
    WHITE: "#FFFFFF",
    BLACK: "#000000",
    badColor: "#hoge" // 🚫 타입 오류 ("badColor"가 대문자가 아님)
}

// satisfies 사용
const COLOR_SATISFIES = {
    WHITE: "#FFFFFF",
    BLACK: "#000000",
    badColor: "#hoge" // 🚫 타입 오류 ("badColor"가 대문자가 아님)
} satisfies Record<ColorKey, string>

 

이 두 방법은 겉보기에는 비슷하지만, 중요한 차이가 있습니다.

 

satisfies를 사용하는 방법이 더 유리한 이유를 살펴보겠습니다.

 

무엇이 다른가?

COLOR_ANNOTATECOLOR_SATISFIES 간의 차이를 살펴보면, "객체의 프로퍼티에 타입 안전하게 접근할 수 있는가"에서 차이가 발생합니다.

// 타입 주석 사용
const COLOR_ANNOTATE: Record<ColorKey, string> = {
    WHITE: "#FFFFFF",
    BLACK: "#000000",
    badColor: "#hoge" // 🚫 타입 오류
}

// satisfies 사용
const COLOR_SATISFIES = {
    WHITE: "#FFFFFF",
    BLACK: "#000000",
    badColor: "#hoge" // 🚫 타입 오류
} satisfies Record<ColorKey, string>

// 타입 안전하게 접근 불가
const selectedColorAnnotate = COLOR_ANNOTATE.????

// 타입 안전하게 접근 가능
const selectedColorSatisfies = COLOR_SATISFIES.BLACK

 

왜 이런 차이가 발생하는지 조금 더 깊게 알아보겠습니다.

 

COLOR_ANNOTATE의 타입

COLOR_ANNOTATE는 타입 주석을 사용하여 Record<Uppercase<string>, string> 타입을 가지게 됩니다

 

하지만 Uppercase<string>은 결국 문자열이므로 최종적으로 {[x: string]: string;} 타입이 됩니다.

 

이는 키가 단순히 string 타입이기 때문에 어떤 문자열이든 지정 가능하여 타입 안전성이 떨어집니다.

 

COLOR_SATISFIES의 타입

반면, COLOR_SATISFIESsatisfies를 활용하여 타입 주석을 하지 않았기 때문에 타입 추론에 의해 객체의 실제 타입이 결정됩니다.

 

따라서 다음과 같은 타입을 갖게 됩니다.

{
    WHITE: string;
    BLACK: string;
}

 

실제 값에 기반하여 타입이 추론되므로, 키의 타입이 구체적인 string 리터럴 타입이 되어 각 프로퍼티에 타입 안전하게 접근할 수 있게 됩니다.

 

다양한 타입과의 조합

Uppercase의 다양한 활용

앞에서 Uppercase<T>와의 조합 예시를 보았지만, 이 외에도 다음과 같은 타입 함수들이 있습니다.

  • Lowercase<T>
  • Capitalize<T>
  • Uncapitalize<T>

이 함수들은 이름 그대로의 역할을 합니다.

 

이러한 함수들의 자세한 사용법이나 intrinsic 키워드에 대해 더 알고 싶으신 분들은 관련 자료를 찾아보시면 좋겠습니다.

 

TemplateLiteral 타입

위의 타입 함수 외에도 TemplateLiteral 타입과 satisfies를 함께 사용해보는 것도 좋습니다.

 

Uppercase의 예시처럼 다음과 같이 활용할 수 있습니다.

type ErrorMessageKey = `${number}_Error`

const ErrorMessage = {
    "404_Error": "요청하신 페이지를 찾을 수 없습니다",
    "418_Error": "저는 티포트라서 커피는 좀...",
} satisfies Record<ErrorMessageKey, string>

 

이처럼 다양한 상황에서 활용할 수 있을 것입니다.

마무리

오늘은 satisfiesUppercase를 활용하여 타입을 강화하는 방법을 소개했습니다.

 

이 외에도 제가 좋아하는 satisfies의 활용법이 몇 가지 더 있으니, 다른 글에서도 소개할 수 있으면 좋겠습니다.

 

앞으로도 다양한 활용법을 공유할 수 있도록 하겠습니다.

 

감사합니다!