[이펙티브 타입스크립트] 아이템17 ~ 아이템20

Yongwoo Cho·2022년 6월 1일
0

TIL

목록 보기
78/98
post-thumbnail

[아이템17] 변경 관련된 오류 방지를 위해 readonly 사용하기

function arraySum(arr: readonly number[]) {
  let sum = 0, num;
  whjile((num = arr.pop()) !== undefined) {
    // ❌ 'readonly number[]'형식에 'pop' 속성이 없습니다.
    sum += num;
  }
  return sum;
}

readonly number[]과 number[]의 차이점

  • 배열의 요소를 읽을 수 있지만, 쓸 수는 없다.
  • length를 읽을 수 있지만, 바꿀 수는 없다.
  • 배열을 변경하는 pop을 비롯한 다른 메서드를 호출할 수 없다.

❗ 변경 가능한 배열을 readonly 배열에 할당할 수는 있지만 반대는 불가능하다.

// 위의 arraySum 고치기
function arraySum(arr: readonly number[]) {
  let sum = 0;
  for (const num of arr) {
    sum += num;
  }
  return sum;
}

만약 함수가 매개변수를 변경하지 않는다면, readonly로 선언하자. 어떤 함수를 readonly로 만들면, 그 함수를 호출하는 다른 함수도 모두 readonly로 만들어야 한다.

인덱스 시그니처에도 readonly를 사용하면 객체의 속성이 변경되는 것을 방지할 수 있다.

interface Array<T> {
  // ...
  [n: number]: T;
}
const xs = [1, 2, 3];
const x1 = xs["1"]; // ❌ 에러 (인덱스 식이 number형식이여야 함)

👍 타입 체크 시점에 오류를 잡을 수 있어서 유용함

배열 순회 방법

  • 인덱스에 신경 쓰지 않을 때 : for-of
  • 인덱스의 타입이 중요할 때 : Array.prototype.forEach
  • 루프 중간에 멈춰야할 때 : for(;;) 루프

❗ 타입이 불확실하다면 for-in 루프는 for-of 또는 C스타일 for 루프에 비해 몇 배나 느림

어떤 길이를 가지는 배열과 비슷한 형태의 튜플을 사용하고 싶다면 타입스크립트에 있는 ArrayLike 타입을 사용한다.

[아이템18] 매핑된 타입을 사용하여 값을 동기화하기

const REQUIRES_UPDATE: { [k in keyof ScatterProps]: boolean } = {
  xs: true,
  ys: true,
  xRange: true,
  yRange: true,
  color: true,
  onClick: false,
};

function shouldUpdate(oldProps: ScatterProps, newProps: ScatterProps) {
  let k: keyof ScatterProps;
  for (k in oldProps) {
    if (oldProps[k] !== newProps[k] && REQUIRES_UPDATE[k]) {
      return true;
    }
  }
  return false;
}

[k in keyof ScatterProps]는 타입체커에게 REQUIRES_UDPATE가 ScatterProps와 동일한 속성을 가져야 한다는 정보를 제공한다.

매핑된 타입은 한 객체가 또 다른 객체와 정확히 같은 속성을 가지게 할 때 이상적이다

  • 매핑된 타입을 사용해서 관련된 값과 타입을 동기화하도록 하자
  • 인터페이스에 새로운 속성을 추가할 때, 선택을 강제하도록 매핑된 타입을 고려하자

[아이템19] 추론 가능한 타입을 사용해 장황한 코드 방지하기

타입 추론이 된다면 명시적 타입 구문을 필요하지 않다

Product 타입과 기록을 위한 함수

interface Product {
  id: number;
  name: string;
}
function logProduct(product: Product) {
  const id: number = product.id;
  const name: string = product.name;
}

후에 id에 문자도 들어 있을 수 있음을 알게 되었을 때 Product내의 id 타입을 변경하면 logProduct 내의 id 변수 선언에서 에러가 발생한다.

👉 비구조화 할당문을 사용하여 구현하는 것이 낫다

function logProduct(product: Product) {
  const { id, name } = product;
}

비구조화 할당문은 모든 지역변수의 타입이 추론되도록 한다.

이상적인 타입스크립트 코드는 함수/메서드 시그니처에 타입 구문을 포함하지만, 함수 내에서 생성된 지역 변수에는 타입 구문을 넣지 않는다

타입이 추론될 수 있음에도 여전히 타입을 명시하고 싶은 경우

  • 객체 리터럴을 정의할 때
    정의에 타입을 명시하면 잉여 속성 체크가 동작함
  • 함수의 반환타입을 명시할 때
    타입 추론이 가능할지라도 구현상의 오류가 함수를 호출한 곳까지 영향을 미치지 않도록 하기 위해 타입 구문을 명시하는 것이 좋다. 또한 반환 타입을 명시하면 함수에 대해 더욱 명확하게 알 수 있다.

[아이템20] 다른 타입에는 다른 변수 사용하기

변수의 값은 바뀔 수 있지만 그 타입은 보통 바뀌지 않는다

다른 타입에는 별도의 변수를 사용하는게 바람직한 이유

  1. 서로 관련이 없는 두 개의 값을 분리한다.
  2. 변수명을 더 구체적으로 지을 수 있다.
  3. 타입 추론을 향상시키며, 타입 구문이 불필요해진다.
  4. 타입이 간결해진다.
  5. let 대신 const로 변수를 선언하게 된다. const로 변수를 선언하면 코드가 간결해지고, 타입 체커가 타입을 추론하기에도 좋다.
profile
Frontend 개발자입니다 😎

0개의 댓글