이펙티브 타입스크립트 | 6장 타입 선언과 @types

dev_hee·2023년 2월 20일
0

TypeScript

목록 보기
6/6

본 문은 이펙티브 타입스크립트를 읽고 알게된 내용을 정리한 글입니다.


[아이템 45] devDependencies에 typescript와 @types 추가하기

npm (node package manager)

npm은 자바스크립트 라이브러리 저장소와 프로젝트가 의존하고 있는 라이브러리들의 버전을 지정하는 방법(package.json)을 제공한다.

세 가지 종류의 의존성이 구분된다. (package.json)

dependencies

현재 프로젝트를 실행하는데 필수적인 라이브러리들이 포함된다.
런타임에 사용된 라이브러리가 해당한다.
프로젝트를 npm에 공개해 다른 사용자가 해당 프로젝트를 설치하면 dependencies 에 있는 라이브러리도 함께 설치된다. 이런 현상을 전이(transitive)의존성이라고 한다.

devDependencies

현패 프로젝트를 개발하고 테스트하는데 사용되지만, 런타임에는 필요없는 라이브러리가 포함된다.
프로젝트를 npm에 공개해 다른 사용자가 해당 프로젝트를 설치하면 devDependencies 에 있는 라이브러리는 제외된다.

peerDependecies

런타임에 필요하긴 하지만 의존성을 직접 관리하지 않는 라이브러리가 포함된다.

타입스크립트

타입스크립트는 개발 도구이므로 devDependencies에 속한다.

타입스크립트 프로젝트에서 고려해야 할 의존성 두 가지가 있다.

타입스크립트 자체 의존성

타입스크립트를 시스템 레벨(전역)로 설치할 수 있지만 추천하지 않는다.

  • 팀원 모두가 항상 동일한 버전을 설치한다는 보장이 없다.
  • 프로젝트를 셋업할 때 별도의 단계가 추가된다.

타입스크립트를 시스템 레벨로 설치하지 말고 devDependencies에 넣는것이 좋다.

타입 의존성(@types)

사용하는 라이브러라기 타입 선언을 포함하지 않더라도, DefinitelyTyped에서 타입 정보를 얻을 수 있다. DefinitelyTyped의 타입 정의들은 npm 레지스트리의 @types 스코프에 공개된다.

@types/jquery
@types/lodash

@type 라이브러리는 타입 정보만 포함하고 있으며 구현체는 없다.

원본 라이브러리는 dependecies에 있더라도 @type 의존성은 devDependencies에 있어야 한다.

리액트는 다음처럼 실행한다.

npm install react
npm install --save-dev @types/react

그러나 타입 의존성을 devDependencies에 넣는 방식이 항상 유효한 것은 아니다. 바로 다음 아이템 46에서 다룬다.


[아이템 46] 타입 선언과 관련된 세 가지 버전 이해하기

타입스크립트를 사용하면 의존성 관리가 더 복잡해진다.

  • 라이브러리 버전
  • 타입 선언(@types)의 버전
  • 타입스크립트의 버전

세 가지 버전 중 하나라도 맞지 않으면 의존성과 상관없어 보이는 곳에서 엉뚱한 오류가 발생할 수 있다.

라이브러리와 타입 정보의 버전이 별도로 관리되는 방식은 다음과 같은 문제점이 있다.

1. 라이브러리를 업데이트 했지만 실수로 타입 선언은 업데이트 하지 않은 경우

라이브러리 업데이트와 관련된 새로운 기능을 사용하려 할 때마다 타입 오류가 발생하게 된다. 하위 호환성이 깨지는 변경이 있다면 타입 체커는 통과해도 런타임 오류가 생길 수 있다.

해결법

  1. 타입 선언도 업데이트 하여 라이브러리와 버전을 맞춘다.
  2. 업데이트할 타입 선언이 아직 준비되어 있지 않다면, 보강 기법을 사용해 새 함수와 메서드 타입정보를 프로젝트 자체에 추가한다.
  3. 또는 타입 선언의 업데이트를 직접 작성하고 공개하여 커뮤니티에 기여한다.

2. 라이브러리보다 타입 선언의 버전이 최신인 경우

타입 정보 없이 라이브러리를 사용해 오다가, 탕비 선언을 설치하려고 할 때 뒤늦게 발생한다.
라이브러리와 타입 선언의 버전이 맞도록 라이브러리 버전을 올리거나 타입 선언의 버전을 내린다.

3. 프로젝트에서 사용하는 타입스크립트 버전보다 라이브러리에서 필요로 하는 타입스크립트 버전이 최신인 경우

이 오류를 해결하려면 프로젝트의 타입스크립트 버전을 올리거나, 라이브러리 타입 선언의 버전을 내리거나, declare module 선언으로 라이브러리 타입 정보를 없애버리면 된다.

타입스크립트의 특정 버전에 대한 타입 정보를 설치하려면 다음을 실행한다.

npm install --save-dev @types/lodash@ts3.1

4. @types 의존성이 중복될 수도 있다.

만약 @types/foo 와 @types/bar에 의존하는 경우를 예로 들자.
@types/bar가 현재 프로젝트와 호환되지 않는 버전의 @types/foo에 의존한다면 npm 은 중첩된 폴더에 별도로 해당 버전을 설치해 문제를 해결하려 한다.
런타임에 사용되는 모듈이라면 괜찮다.

하지만 전역 네입스페이스에 있는 타입 선언 모듈이라면 문제가 발생한다. @types/foo 나 @types/bar를 업데이트해서 서로 버전이 호환되게 하여 해결할 수 있다.

번들링된 타입

일부 라이브러리들은 자체적으로 타입 선언을 포함(번들링)하게 된다. 자체적인 타입 선언은 보통 package.json의 "types"에서 .d.ts 파일을 가리키도록 되어있다. 이런 경우 버전 불일치 문제를 해결하기는 한다.

이렇게 라이브러리가 타입스크립트로 작성되고 컴파일러를 통해 타입 선언이 생성된 경우, 즉 번들링하여 타입 선언을 포함하는 경우엔 문제점을 가지고 있다.

1. 번들된 타입 선언에 보강 기법으로 해결할 수 없는 오류가 있는 경우, 또는 공개 시점에서는 잘 동작했지만 타입스크립트 버전이 올라가면서 오류가 발생하는 경우
@types를 별도로 사용하면 라이브러리 자체의 버전에 맞춰 선택할 수 있다. 하지만 번들된 타입은 버전 선택이 불가능하다.

2. 프로젝트 내의 타입 선언이 다른 라이브러리 타입 선언에 의존하는 경우

보통 의존성이 devDependecies에 들어가는데, 프로젝트를 공개해 다른 사용자가 설치하면 devDependecies가 설치되지 않을 것이고 타입 오류가 발생하게 된다. 아이템 51에서 이런 상황에대한 해결책을 다룬다.
만약 @types를 사용하는 경우엔 문제가 되지 않는다.

3. 프로젝트 과거 버전에 있는 타입 선언에 문제가 있는 경우 과거 버저으로 돌아가서 패치 업데이트를 해야한다.

하지만 DefinitelyTyped는 동일 라이브러리의 여러 버전의 타입 선언을 동시에 유지보수할 수 있다.

4. 타입 선언의 패치 업데이트를 자주하기 어렵다.

DefinitelyTyped는 커뮤니티에서 관리되어 타입선언에 대한 잦은 패치를 관리하기 쉽다.

라이브러리를 공개하는 경우, 타입 선언을 자체적으로 포함할지, 타입 정보만 분리해 DefinitelyTyped에 공개할지 장단점을 비교해야한다. 일반적으론 다음과 같다.

  • 타입스크립트로 작성된 경우: 라이브러리에 타입선언을 포함
  • 자바스크립트로 작성된 경우: 직접 타입을 작성해야하므로 DefinitelyTyped에 공개

[아이템 47] 공개 API에 등장하는 모든 타입을 익스포트하기

interface SecretName {
  first: string
  last: string
}

interface SecretSanta {
  name: SecretName
  gift: string
}

export function getGift(name: SecretName, gift: string): SecretSanta {
  return {
    name: {
      first: 'Dan',
      last: 'Van',
    },
    gift: 'MacBook Pro',
  }
}

어떤 라이브러리가 위의 코드처럼 SecretName, SecretSanta를 익스포트 하지 않았다.
getGift만 직접 임포트해서 사용할 수 있다.

하지만 타입들은 익스포트된 함수 시그니처에 등장하기 때문에 추출해낼 수 있다.

type MySanta = ReturnType<typeof getGift> // SecretSanta
type MyName = Parameters<typeof getGift>[0] // SecretName

[아이템 48] API 주석에 TSDoc 사용하기

타입스크립트 언어 서비스가 JSDoc 스타일을 지원하기 때문에 적극적으로 활요하는것이 좋다. 타입스크립트에선 TSDoc 이라고 부르기도 한다.

@param과 @returns

함수를 호출하는 부분에서 각 매개변수에 관련된 설명을 보여준다. 예제

/**
 * Generate a greeting.
 * @param name Name of the person to greet
 * @param salutation The person's title
 * @returns A greeting formatted for human consumption.
 */
function greetFullTSDoc(name: string, title: string) {
  return `Hello ${title} ${name}`
}

타입 정의에 TSDoc

객체의 각 필드에 마우스를올려 빌드별 설명을 볼 수 있다. 예제

interface Vector3D {}
/** A measurement performed at a time and place. */
interface Measurement {
  /** Where was the measurement made? */
  position: Vector3D;
  /** When was the measurement made? In seconds since epoch. */
  time: number;
  /** Observed momentum */
  momentum: Vector3D;
}

마크다운 제공

굵은 글씨(**text**), 기울임 글씨, 글머리기호 목록을 사용할 수 있다. 예제

주석에 타입 정보를 포함하지 말자. 타입정보는 타입스크립트에서 제공해야한다.


[아이템 49] 콜백에서 this에 대한 타입 제공하기

5


[아이템 50] 오버로딩 타입 보다는 조건부 타입 사용하기

3


[아이템 51] 의존성 분리를 위해 미러 타입 사용하기

2


[아이템 52] 테스팅 타입의 함정에 주의하기

6

profile
🎨그림을 좋아하는 FE 개발자👩🏻‍💻

0개의 댓글