본문 바로가기
IT/typescript

[Typescript] 타입 호환성

by 내일은교양왕 2024. 2. 22.

Typescript의 타입 호환성

> 구조적 서브타이핑 기반

 

구조적 서브타이핑

> 오직 멤버만으로 타입을 관계시키는 방식

> y가 최소환 x와 동일한 멤버를 가지고 있다면, x와 y는 호환된다.

> '덕 타이핑'이라고도 함 '만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를것이다'

type Food = {
  /** 각 영양소에 대한 gram 중량값 */
  protein: number;
  carbohydrates: number;
  fat: number;
}

type Burger = Food & {
  /** 햄버거 브랜드 이름 */
  burgerBrand: string;
}

function calculateCalorie(food: Food) {
  return food.protein * 4
    + food.carbohydrates * 4
    + food.fat * 9
}

const burger: Burger = {
  protein: 29,
  carbohydrates: 48,
  fat: 13,
  burgerBrand: '버거킹'
}

const calorie = calculateCalorie(burger)
/** 타입검사결과 : 오류없음 (OK) */

 

 

fresh object를 직접 calculateCalorie() 함수에 넣으면 컴파일 에러 발생

> typescript에서 컴파일할 때 FreshLiteral 여부를 확인. Fresh하다면 컴파일 에러 발생

> type assertion하거나 타입추론에 의해 타입이 확정되면 freshness 사라짐

> 이렇게 하는 이유는 코드를 읽는 다른 개발자의 입장에서 함수가 실제 다루는 것보다 더 많은 데이터를 받아들인다는 오해를 불어일으킬 수 있고, 프로퍼티 키에 대한 오타가 발생하더라도 오류가 확인되지 않는 부작용 때문

> 그럼에도 불구하고 fresh object를 사용하고 싶다면 tsconfig에서 suppressExcessPropertyErrors: true로 변경

 

const calorie = calculateCalorie({
  protein: 29,
  carbohydrates: 48,
  fat: 13,
  burgerBrand: '버거킹'
})
/** 타임검사결과 : 오류 (NOT OK)*/

/** 부작용 1
 * 코드를 읽는 다른 개발자가 calculateCalorie 함수가
 * burgerBrand를 사용한다고 오해할 수 있음 */
const calorie1 = calculateCalorie({
  protein: 29,
  carbohydrates: 48,
  fat: 13,
  burgerBrand: '버거킹'
})

/** 부작용 2
 * birgerBrand 라는 오타가 발생하더라도
 * excess property이기 때문에 호환에 의해 오류가
 * 발견되지 않음 */
const calorie2 = calculateCalorie({
  protein: 29,
  carbohydrates: 48,
  fat: 13,
  birgerBrand: '버거킹'
})

 

 

반대로 타입 호환을 허용하지 않도록 강제하는 것도 가능

> branded type 사용

 

 

https://toss.tech/article/typescript-type-compatibility

 

TypeScript 타입 시스템 뜯어보기: 타입 호환성

타입호환성은 무엇이며 왜 필요할까요? 타입호환이 지원되지 않는 경우가 존재한다는 것을 아셨나요? 평소 익숙했던 개념들에 대해 질문을 던져가며 TypeScript 타입 시스템에 관해 심도있게 알아

toss.tech