📖

[요약 정리] Effective TypeScript

태그
요약프론트엔드독서
최종 편집
Dec 30, 2022 2:33 AM
발행일
November 30, 2021

Dan Vanderkam의 책 Effective TypeScript: 62 Specific Ways to Improve Your TypeScript 내용 중 메모할 부분만 기록한다. 책 내용이 무척 좋다.

완독 후 간단 리뷰

  • 완독해야만 제대로 학습한 것이라는 집착은 이제 별로 없지만 그래도 처음부터 끝까지 읽었을 때의 묘한 기쁨이 있다.
  • 내가 지금까지 읽은 타입스크립트 글을 통틀어 가장 괜찮은 녀석이었다. 자바스크립트로 된 프로젝트를 타입스크립트로 바꾸고 싶은 팀이라면 크게 도움이 될 좋은 책이다. 타입스크립트뿐 아니라 자바스크립트의 원리도 이해하기 쉽게 설명해주어서, 사실 웹개발자라면 누구라도 읽어도 좋을 거라고 본다.
  • FE재남님이 유튜브에 올리시는 <자바스크립트 딥 다이브> 스터디에서 알게 된 내용도 책에 종종 나와서, 묘하게 복습하는 기분의 시너지가 있었다.
  • 다음 책은 원래 Software Engineering at Google로 계획했다가, 최근 본 Talks at Google 강연이 무척 끌려서 A Philosophy of Software Design 으로 정했다. 마침 2021년에 개정판도 나왔더라.

Item 9: Prefer Type Declarations to Type Assertions

  • 함수 타이핑할 때 (name: Person)(name): Person의 차이. 후자는 리턴값까지 타이핑해준다.
  • type assertion은 웬만하면 피하고, 타입스크립트보다 개발자가 더 많이 알고 있을 때 쓰면 좋다. 예를 들어 타입스크립트는 DOM 정보는 모름

Item 10: Avoid Object Wrapper Types (String, Number, Boolean, Symbol, BigInt)

  • object wrapper의 개념은 몰랐다. primitive는 스스로 가지는 메서드가 없음.
    • 메서드는 프로토타입이 가지는 건데 프리미티브는 프로토타입이 없기 때문.
    • 따라서 메서드 호출은 사실 인스턴스로 감싸서 해준 다음 폐기하는 것이다. String(“abc”).charAt(0)
    • charAt은 String.prototype 에 정의되어 있음

Item 11: Recognize the Limits of Excess Property Checking

  • const x: T = …const x; const y: T = x 의 컴파일 결과가 다름
  • excess prop check는 obj literal에 대해서만 함. 이건 실제로 런타임 에러를 발생시키는 건 아니나 개발자가 실수했을 수 있는 부분을 발견해주는 것이다.

Item 13: Know the Differences Between type and interface

  • 시간이 지나면서 둘 사이의 차이가 거의 없어짐
  • 인터페이스는 union 같은 complex type을 extend 못함
  • 일반적으로는 타입 쓰는 게 나음
  • 인터페이스는 augment 가능. declaration merge 가능하다는 것 때문에 외부 공개용 패키지나 타입에서는 인터페이스로 많이 씀.

Item 16: Prefer Arrays, Tuples, and ArrayLike to number Index Signatures

  • object key는 언제나 string으로 바뀐다. num으로 넣어도 알아서 바뀜.
  • array도 js에서는 오브젝트이므로 array index도 사실은 스트링이다. object.keys로 어레이 넣어보면 스트링 나옴.
  • 하지만 ts에서는 number 타입으로 index를 유지함. 이건 유용한 fiction이지만, fiction임을 기억하고 있어야 함

Item 17: Use readonly to Avoid Errors Associated with Mutation

  • readonly 붙으면 타입 자체가 달라짐! 몰랐다.
    • You can read from its elements, but you can’t write to them.
    • You can read its length, but you can’t set it (which would mutate the array).
    • You can’t call pop or other methods that mutate the array.
    • TypeScript checks that the parameter isn’t mutated in the function body.
    • Callers are assured that your function doesn’t mutate the parameter.
    • Callers may pass your function a readonly array.

Item 19: Avoid Cluttering Your Code with Inferable Types

Item 21: Understand Type Widening

  • Type Widening: 변수에 값을 넣어 초기화할때 그 값 자체보다 얼마나 더 넓은 타입을 지정하는가.
  • const를 쓰면 프리미티브의 타입은 효과적으로 좁아지지만 그래도 오브젝트라면 타입 시스템이 추론하기 어려움
  • ts의 기본 동작을 오버라이드하는 방법
    • explicit type annotation
    • as const - narrowest possible
    • context 추가하기 (함수 파라미터로 사용한다거나) → 이후 아이템에서 설명

Item 24: Be Consistent in Your Use of Aliases

  • type aliasing은 컨트롤 플로우 분석을 깰 수 있다. destructure하면 되지만, 그러면 값이 달라져버릴 수 있는 위험도 있다는 것은 명심.
type X = { y?: number }

const x: X;

if (x.y) {} // x.y가 존재함이 보장됨

const y = x.y;
if (y) {} // x.y가 존재함이 보장되지 않음

const { y } = x;
if (y) {} // y가 존재함이 보장되나, x[y] = ... 로 새로 할당이 가능하므로 원본과 달라질 수 있음

Item 25: Use async Functions Instead of Callbacks for Asynchronous Code

  • Promise.race 로 타임아웃 구현이 가능. 처음 알았다.

Item 29: Be Liberal in What You Accept and Strict in What You Produce

  • 제목 자체가 좋음
  • Array, ArrayLike처럼 인풋은 더 넓게 받아주면 사용부애서 편함. 브레이킹 체인지도 적어질듯
  • 근데 이게 항상 좋은지는 모르겠다. 사용하는 방식이 일관적이지 않게 될듯

Item 30: Don’t Repeat Type Information in Documentation

  • mutate가 일어나면 안 되는 파라미터면 readonly 붙여라
  • 타입만으로 알기 어려운 정보는 이름에 추가하라. unit이라든가

Item 34: Prefer Incomplete Types to Inaccurate Types

  • 타입을 너무 좁히려다가 부정확하게 될바에는 넓게 해라

Item 37: Consider “Brands” for Nominal Typing

  • _brand 를 덧붙이고 type guard를 만듦으로써 실제로 오브젝트 키를 추가하지 않고 타입 시스템만으로 안전하게 할 수 있음

Item 44: Track Your Type Coverage to Prevent Regressions in Type Safety

  • noImplcitAny 써도 explicit any와 써드파티에서의 any는 못 막음
  • npx type-coverage로 any가 아닌 타입 정의 비율 볼 수 있음

Item 46: Understand the Three Versions Involved in Type Declarations

  • @types 패키지 설치하면 세가지 문제 있을 수 있음
    • 라이브러리가 타입보다 더 최신이거나, 타입이 라이브러리보다 최신
      • 패치 버전만 바뀌면 이론상 문제 없어야 하지만 아닐 수도 있음. 버전 싱크 필요
    • 타입이 새 타입스크립트 버전을 필요로 할 때
      • ts 버전을 고정시킬 수 있음 ‘npm install --save-dev @types/lodash@ts3.1’
    • 타입 패키지 a가 b에 의존하는데 b도 직접 깔았다거나 해서 둘이 호환 안되는 버전일때
      • npm ls 로 트랙 다운 가능
      • transitive라서 문제. 직접 싱크 맞춰야 함

Item 48: Use TSDoc for API Comments

  • 함수에(public api) 주석 달 때는 그냥 인라인 코멘트보다는 jsdoc이나 tsdoc 스타일로 달면 vscode에서 잘 보인다
  • param, returns, markdown 사용 가능
  • /**
     * Generate a **greeting**.
     * @param name Name of the person to greet
     * @param salutation The person's title
     * @returns A greeting formatted for human consumption.
     */

Item 51: Mirror Types to Sever Dependencies

  • Use structural typing to sever dependencies that are nonessential.
  • Don’t force JavaScript users to depend on @types. Don’t force web developers to depend on NodeJS.

Item 52: Be Aware of the Pitfalls of Testing Types

Item 53: Prefer ECMAScript Features to TypeScript Features

  • 기본적으로 타입스크립트는 타입 시스템일 뿐이고 자바스크립트로 컴파일된 결과에 영향을 안 미쳐야 하지만, 몇 가지 예외가 있다. ECMAScript에서 스펙이 정의되기 전에 타입스크립트가 이들을 지원하려고 추가했던 기능들의 잔재다.
  • 이것들을 쓸 때 조심하고, 되도록이면 네이티브 기능을 쓰자.
    • enum
    • 클래스에서 파라미터 프로퍼티
    • 네임스페이스, ///
    • 데코레이터

Item 56: Don’t Rely on Private to Hide Information

  • private 키워드는 타입 시스템에 불과함
  • 정보를 진정으로 숨기려면 클로저 쓸 것
  • 또는 새로 나온 문법인 #으로 정의하는 걸 쓸 수도 있음
class PasswordChecker {
  #passwordHash: number;

  constructor(passwordHash: number) {
    this.#passwordHash = passwordHash;
  }

  checkPassword(password: string) {
    return hash(password) === this.#passwordHash;
  }
}

const checker = new PasswordChecker(hash('s3cret'));
checker.checkPassword('secret');  // Returns false
checker.checkPassword('s3cret');  // Returns true