[Typescript]브랜드 타입(Branded type)

Choise.o·2024년 6월 9일
0

이전 포스팅을 쓰다가 브랜드 타입에 대해 알게 되어서 간략하게 정리해본다.

1. branded type

  • branded type 또는 branding type이라고 한다.
  • 구조적 타입 시스템을 사용하는 typescript에서 명목적 타입시스템을 사용하는 것처럼 타입을 정의 할 수 있다.
  • 원시 타입인 값의 타입 구분 등을 위해 사용된다.

2. 사용법

  • __brand 속성으로 브랜딩 타입을 정의하여 사용할 수 있다.
  • 기본 사용법 : type Brand<K, T> = K & {__brand: T}

/**
* 브랜드 타입을 사용하지 않았을 때
*/

// number 타입의 두 변수를 선언한다
const euro1 = 1;
const won1 = 2;

const euroToUsd = (euro: number) => {
    return euro * 10;
}

const wonToUsd = (won: number) => {
    return won * 20;
}

euroToUsd(won1); // OK
wonToUsd(euro1); // OK
// ==> euro를 인자로 받는 함수에 won을 넘겨도 에러가 발생하지 않는다. 반대의 경우도 마찬가지이다.


/**
* 브랜드 타입 사용
*/
type Brand<K, T> = K & {__brand: T}; // 브랜딩 타입 정의 : __brand 속성을 추가해준다.


type Euro = Brand<number, 'Euro'>;
type Won = Brand<number, 'Won'>;

const brandEuroToUsd = (euro: Euro) => {
    return euro * 10;
}

const brandWonToUsd = (won: Won) => {
    return won * 20;
}

const euro2 = 10 as Euro;
const won2 = 20 as Won;


brandEuroToUsd(won2); // ERROR : Argument of type 'Won' is not assignable to parameter of type 'Euro'
brandWonToUsd(euro2); // ERROR : Argument of type 'Euro' is not assignable to parameter of type 'Won'.
// ==> euro를 인자로 받는 함수에 won을 넘기면 에러가 발생한다. 
// ==> 같은 number 타입을 인자롤 받더라도 타입을 구분하여 사용할 수 있다.

brandEuroToUsd(euro2);  // OK
brandWonToUsd(won2);    // OK

3. 타입 호환성 제한

  • 브랜드 타입을 사용하여 타입 호환성을 제한할 수 있다.
  • 아래 코드처럼 이런저런 조건을 바꿔보며 여러 케이스를 테스트해봤는데 꽤나 엄격하게 타입 호환성을 제한한다. 유연성이 많이 떨어지므로 정말 필요한 경우가 아니라면 브랜드 타입은 가급적 사용하지 않는게 좋을 것 같다.
type Brand<K, T> = K & {__brand: T};
type Food = Brand<{
    protein: number;
    carbon: number;
    fat: number;
}, 'Food'>


// 브랜드 타입을 인자로 받는 함수
function calculateCalories(food: Food) {
    return food.protein * 4 + food.carbon * 4 + food.fat * 9;
}

/**
* 에러가 발생하는 경우
*/
// 1) 브랜드 타입 정의 시 사용한 객체의 프로퍼티만 작성했을 때
const food1 = {
    protein: 10,
    carbon: 10,
    fat: 10
}

calculateCalories(food1); // ERROR

// 2) 변수 타입을 지정해줬을 때
const food2: Food = {
    protein: 15,
    carbon: 15,
    fat: 15
}

calculateCalories(food2); // ERROR(변수 선언부터 이미 에러)


/**
* 정상 동작하는 경우
*/
// 3) 타입 단언해줬을 때
const food3 = {
    protein: 20,
    carbon: 20,
    fat: 20
} as Food

calculateCalories(food3); // OK

// 4) 브랜드 직접 지정해줬을 때
const food4: Food = {
    protein: 20,
    carbon: 20,
    fat: 20,
    __brand: 'Food'
}

calculateCalories(food4); // OK

4. 참고

0개의 댓글