[Effective TypeScript] 2장 9-11 타입 스크립트의 타입시스템

채동기·2023년 2월 14일
0

TypeScript

목록 보기
3/21

아이템 9) 타입 단언보다는 타입 선언 사용하기

interface Person { name: string };

const alice: Person = { name: 'Alice' };  //타입 선언 
const bob = { name: 'Bob' } as Person;  // 타입 단언 
const alice: Person = {};
   // ~~~~~ 'Person' 유형에 필요한 'name' 속성이 '{}' 유형에 있습니다. 
const bob = {} as Person;  // 오류 없음

타입 선언은 할당되는 값이 해당 인터페이스를 만족하는지 검사합니다.
타입 단언은 강제로 타입을 지정했으니 타입 체커에게 오류를 무시하라고 하는 것입니다.
타입 속성을 추가할 때도 마찬가지 입니다.

const alice: Person = {
    name: 'Alice',
    occupation: 'TypeScript developer'
  // ~~~~~~~~~ 개체 리터럴은 알려진 속성만 지정할 수 있으며, 'Person' 형식에 'occupation'이 없습니다.
  };
  const bob = {
    name: 'Bob',
    occupation: 'JavaScript developer'
  } as Person;  // 오류 없음

화살표 함수의 타입선언은 추론된 타입이 모호할 경우가 있습니다.

const people = ["alice", "bob", "jan"].map((name) => ({ name }));

Person을 원했지만 경과는 {name: string;}[]...
단언문을 사용하면 문제가 해결되는 것처럼 보이지만 런타임 에러가 발생합니다.

const people = ['alice', 'bob', 'jan'].map(name => ({} as Person)); // 오류 없음

name의 타입과 반환하는 타입을 직접 명시해서 해결할 수 있습니다.

const people: Person[] = ["alice", "bob", "jan"].map(
  (name): Person => ({ name })
);

타입스크립트보다 타입에 대해서 더 잘알고 있을때에는 타입 단언문과 null 아님 단언문을 작성하면 됩니다.

document.querySelector('#myButton').addEventListener('click', e => {
    e.currentTarget // 타입은 EventTarget
    const button = e.currentTarget as HTMLButtonElement;
    button // 타입은 HTMLButtonElement
  });

타입 스크립트 Dom에 접근할 수 없기 때문에 버튼엘리먼트를 알지 못합니다. 우리는 타입스크립트가 알지 못하는 정보를 가지고 있기때문에 여기서는 타입단언문을 사용하는것이 타당합니다.

(!)을 사용해서 null이 아님을 단언할 수 있습니다.

const elNull = document.getElementById('foo');  // 타입 HTMLElement | null
const el = document.getElementById('foo')!; // 타입 HTMLElement

아이템10) 객체 래퍼 타입 피하기

타입스크립트는 기본형과 객체 래퍼 타입을 별도로 모델링합니다.

기본 , 객체 래퍼
string, String
number, Number
boolean, Boolean
symbol, Symbol
bigint, BigInt

기본형은 객체타입에 할당 가능하지만 반대로는 할 수 없습니다.

타입스크립트는 객체 래퍼 타입을 지양하고, 대신 기본형 타입을 사용해야 합니다.

아이템 11) 잉여 속성 체크의 한계인지하기

객체 리터럴을 변수에 할당하거나 함수에 매개변수로 전달할 때 잉여 속성 체크가 수행됩니다.

interface Room {
  numDoors: number;
  ceilingHeightFt: number;
}
const r: Room = {
  numDoors: 1,
  ceilingHeightFt: 10,
  elephant: "present", // 객체 리터럴은 알려진 속성만 지정할 수 있으며, Room에 'elephant'가 없습니다.
};

임시 변수 obj를 도입해 보면 알 수 있습니다.
obj는 { numDoors: number;
ceilingHeightFt: number;
elephant: string;}
으로 타입이 추론됩니다.
임시 변수를 사용하여 잉여 속성 체크를 건너뛸 수 있습니다.

const obj = {
  numDoors: 1,
  ceilingHeightFt: 10,
  elephant: "present",
};
const r: Room = obj;

obj 타입은 room의 부분 집합을 포함하므로, 할당가능하며 타입 체커도 통과합니다.
위의 예제 중에 첫번째 예제에서는, 구조적 타입 시스템에서 발생할 수 있는 중요한 종류의 오류를 잡을 수 있도록 '잉여 속성 체크'라는 과정이 수행되었습니다.

인덱스 시그니처를 사용해서 추가적인 속성을 예측하게 해도 됩니다.

interface Options {
  darkMode?: boolean;
  [otherOptions: string]: unknown;
}
const o: Options = { darkmode: true }; // 정상

선택적 속성만 가지는 약한 타입에서도 비슷한 체크가 동작합니다.

interface LineChartOptions {
  logscale?: boolean;
  invertedYAxis?: boolean;
  areaChart?: boolean;
}
const opts = { logScale: true };
const o: LineChartOptions = opts; // 'LineChartOptions'에 '{ logScale: boolean; }' 속성이 없음

출처

<이펙티브 타입스크립트> (댄 밴더캅 지음, 장원호 옮김, 인사이트, 2021)

profile
what doesn't kill you makes you stronger

0개의 댓글