[이펙티브 타입스크립트] 아이템25 ~ 아이템26

Yongwoo Cho·2022년 6월 3일
0

TIL

목록 보기
80/98
post-thumbnail

[아이템25] 비동기 코드에는 콜백 대신 async 함수 사용하기

콜백보다 프로미스나 async/await를 사용해야 하는 이유

  • 콜백보다는 프로미스가 코드를 작성하기 쉬움
  • 콜백보다는 프로미스가 타입을 추론하기 쉬움
function timeout(millis: number): Promise<never> {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject("timeout"), millis);
  });
}
async function fetchWithTimeout(url: string, ms: number) {
  return Promise.race([fetch(url), timeout(ms)]);
}

타입구문이 없어도 fetchWithTimeout의 반환 타입은 Promise로 추론된다.

프로미스를 사용하면 타입스크립트의 모든 타입 추론이 제대로 동작한다

프로미스를 생성하기보다는 async/await를 사용해야 하는 이유

  • 일반적으로 더 간결하고 직관적임
  • async 함수는 항상 프로미스를 반환하도록 강제됨
  • 콜백이나 프로미스를 사용하면 실수로 반동기 코드를 작성할 수 있지만 async를 사용하면 항상 비동기 코드를 작성한다.

❗ async 함수에서 프로미스를 반환하면 또 다른 프로미스로 래핑되지 않는다. 반환 타입은 Promise<Promise<T>>가 아닌 Promise<T>가 된다.

[아이템26] 타입 추론에 문맥이 어떻게 사용되는지 이해하기

type Language = "Javascript" | "Typescript";
function setLanguage(language: Language) {
  /* ... */
}
setLanguage("Javascript"); // 정상

let language = "Javascript";
setLanguage(language); // ❌ 'string' 형식의 인수는 'Language'형식의 매개변수에 할당될 수 없습니다.

'Javascript' 값을 변수로 분리해내면, 타입스크립트는 할당 시점에 타입을 추론한다. 위와 같은 경우는 string으로 추론했고, Language 타입으로 할당이 불가능하므로 오류가 발생한다.

👉 해결방법

  • 타입 선언에서 language의 가능한 값을 제한하기
let language: Language = "Javascript";
setLanguage(language); // 정상
  • language를 상수로 만들기
const language = "Javascript";
setLanguage(language); // 정상

language는 변경할 수 없다고 알려줌으로써 타입스크립트는 language에 대해서 더 정확한 타입인 문자열 리터럴 'Javascript'로 추론이 가능하다.

튜플 사용 시 주의점

function panTo(where: [number, number]) {
  /* ... */
}

panTo([1, 2]); // 정상
const loc = [1, 2];
panTo(loc); // ❌ 'number[]' 형식의 인수는 '[number, number]' 형식의 매개변수에 할당될 수 없습니다.

타입스크립트가 loc의 타입을 number[] 로 추론(길이를 알 수 없는 숫자의 배열)하기 때문에 발생한 에러

👉 해결방법

  • 타입 선언
const loc: [number, number] = [1, 2];
panTo(loc); // 정상
  • 상수 문맥 제공
const loc = [1, 2] as const;
panTo(loc); // ❌ 'readonly [1,2]' 형식은 'readonly'이며 변경 가능한 형식 '[number, number'에 할당할 수 없습니다.

function panTo(where: readonly [number, number]) {}
const loc = [1, 2] as const;
panTo(loc); // 정상

객체 사용 시 주의점

type Language = "Javascript" | "Typescript";
interface GovernedLanguage {
  language: Language;
  organization: string;
}
function complain(language: GovernedLanguage) {
  /* ... */
}
complain({ language: "Typescript", organization: "Microsoft" }); // 정상
const ts = {
  language: "Typescript",
  organization: "Microsoft",
};
complain(ts); // ❌ 에러

ts 객체에서 language의 타입은 string으로 추론된다. 타입 선언을 추가하거나 상수 단언을 사용하여 해결할 수 있다.

콜백 사용 시 주의점

function callWithRandom(fn: (n1: number, n2: number) => void) {
  fn(Math.random(), Math.random());
}
callWithRandom((a, b) => {
  a; // type: number
  b; // type: number
  console.log(a + b);
});

const fn = (a, b) => {
  console.log(a + b); // ❌ 'a', 'b' 매개변수에는 암시적으로 'any' 형식이 포함됩니다.
};
callWithRandom(fn);

const fn = (a: number, b: number) => {
  console.log(a + b);
};
callWithRandom(fn); // 정상

변수를 뽑아서 별도로 선언했을 때 오류가 발생한다면 타입 선언을 추가해야 한다.

profile
Frontend 개발자입니다 😎

0개의 댓글