3장 타입 추론

2024.10.27

추론 가능한 타입을 사용해 장황한 코드 방지하기

  • 타입스크립트가 타입을 추론할 수 있다면 타입 구문을 작성하지 않는 게 좋습니다.
  • 이상적인 경우 함수/메서드의 시그니처에는 타입 구문이 있지만, 함수 내의 지역 변수에는 타입 구문이 없습니다.
  • 추론될 수 있는 경우라도 객체 리터럴과 함수 반환에는 타입 명시를 고려해야 합니다. 이는 내부 구현의 오류가 사용자 코드 위치에 나타나는 것을 방지해 줍니다.
// 함수 반환에 타입 명시 안 한 경우
const cache: { [ticker: string]: number } = {};

function getQuote(ticker: string) {
  if (ticker in cache) {
    return cache[ticker];
  }
  return fetch('https://quotes.example.com/?q=${ticker}')
    .then((response) => response.json())
    .then((quote) => {
      cache[ticker] = quote;
      return quote;
    });
}

getQuote('MSFT').then(considerBuying);
// 'number | Promise<any>' 형식에 'then1 속성이 없습니다.
// 'number' 형식에 'then' 속성이 없습니다.

// 함수 반환에 타입 명시한 경우
const cache: { [ticker: string]: number } = {};

function getQuote(ticker: string): Promise<number> {
  if (ticker in cache) {
    return cache[ticker];
    // 'number' 형식은 ,Promise<number>, 형식에 // 할당할 수 없습니다.
  }
  // ...
}

다른 타입에는 다른 변수 사용하기

  • 변수의 값은 바뀔 수 있지만 타입은 일반적으로 바뀌지 않습니다.
  • 혼란을 막기 위해 타입이 다른 값을 다룰 때에는 변수를 재사용하지 않도록 합니다.

타입 넓히기

  • 타입 넓히기(widening) : 상수를 사용해서 변수를 초기화할 때 타입을 명시하지 않으면 타입 체커가 결정하는 타입. 타입 체커는 지정된 단일 값을 가지고 할당 가능한 값들의 집합을 유추합니다.
  • 타입스크립트가 넓히기를 통해 상수의 타입을 추론하는 법을 이해해야 합니다.
  • 동작에 영향을 줄 수 있는 방법인 const, 타입구문(명시적으로 타입 구문 제공), 문맥(함수의 매개변수로 값을 전달하여 문맥 추가), as const에 익숙해져야 합니다.
const vl = { x: 1, y: 2 }; // 타입은 { x: number; y: number; }
const v2 = {
  x: 1 as const,
  y: 2,
}; // 타입은 { x:1; y:number; }
const v3 = { x: 1, y: 2 } as const; // 타입은 { readonly x: 1; readonly y: 2; }

타입 좁히기

  • 타입 좁히기는 타입스크립트가 넓은 타입으로부터 좁은 타입으로 진행하는 과정을 말합니다.

    • 기본적인 예시는 null 체크
    const el = document.getElementByld('foo'); // 타입이 HTMLElement | null
    
    if (el) {
      el; // 타입이 HTMLElement
      el.innerHTML = 'Party Time'.blink();
    } else {
      el; // 타입이 null
      alert('No element #foo');
    }
  • instanceof , 속성 체크, Array.isArray 와 같은 일부 내장 함수로도 타입 좁히기가 가능합니다.

  • 태그된 유니온(tagged union), 사용자 정의 타입 가드와 같은 기법으로도 타입을 좁힐 수 있습니다.

  • 분기문 외에도 여러 종류의 제어 흐름을 살펴보며 타입스크립트가 타입을 좁히는 과정을 이해해야 합니다.

한꺼번에 객체 생성하기

  • 속성을 제각각 추가하지 말고 한꺼번에 객체로 만들어야 합니다.
  • 안전한 타입으로 속성을 추가하려면 객체 전개 ({...a, ...b})를 사용하면 됩니다.
  • 객체에 조건부로 속성을 추가하는 방법을 익히도록 합니다.

일관성 있는 별칭 사용하기

  • 별칭은 타입스크립트가 타입을 좁히는 것을 방해합니다. 따라서 변수에 별칭을 사용할 때는 일관되게 사용해야 합니다.
function isPointlnPolygon(polygon: Polygon, pt: Coordinate) {
  const box = polygon.bbox;

  if (polygon.bbox) {
    if (pt.x < box.x[0] || pt.x > box.x[l] || pt.y < box.y[0] || pt.y > box.y[l]) {
      // box 객체가 'undefined*일 수 있습니다.
      return false;
    }
  }
  // ...
}
  • 비구조화 문법을 사용해서 일관된 이름을 사용하는 것이 좋습니다.
function isPointlnPolygon(polygon: Polygon, pt: Coordinate) {
  const { bbox } = polygon;

  if (bbox) {
    const { x, y } = bbox;

    if (pt.x < x[0] || pt.x > x[1] || pt.y < y[0] || pt.y > y[1]) {
      return false;
    }
  }
  // ...
}
  • 함수 호출이 객체 속성의 타입 정제를 무효화할 수 있다는점을 주의해야 합니다. 속성보다 지역 변수를 사용하면 타입 정제를 믿을 수 있습니다.

비동기 코드에는 콜백 대신 async 함수 사용하기

  • 콜백보다는 프로미스를 사용하는 게 코드 작성과 타입 추론 면에서 유리합니다.
  • 가능하면 프로미스를 생성하기보다는 async와 await를 사용하는 것이 좋습니다. 간결하고 직관적인 코드를 작성할 수 있고 모든 종류의 오류를 제거할 수 있습니다.
  • 어떤 함수가 프로미스를 반환한다면 async로 선언하는 것이 좋습니다.

타입 추론에 문맥이 어떻게 사용되는지 이해하기

  • 타입스크립트는 일반적으로 값이 처음 등장할 때 타입을 결정합니다.
  • 타입 추론에서 문맥이 어 떻게 쓰이는지 주의해서 살펴봐야 합니다.
  • 변수를 뽑아서 별도로 선언했을 때 오류가 발생한다면 타입 선언을 추가해야 합니다.
  • 변수가 정말로 상수라면 상수 단언(as const)을 사용해야 합니다. 그러나 상수 단언을 사용하면 정의한 곳이 아니라 사용한 곳에서 오류가 발생하므로 주의해야 합니다.

함수형 기법과 라이브러리로 타입 흐름 유지하기

  • 타입 흐름을 개선하고, 가독성을 높이고, 명시적인 타입 구문의 필요성을 줄이기 위해 직접 구현하기보다는 내장된 함수형 기법과 로대시 같은 유틸티 라이브러리를 사용하는 것이 좋습니다.