Published on

타입 선언과 @types - Item 45 ~ 47

타입 선언과 @types - Item 45 ~ 47

Item 45) devDependencies에 typescript와 @types 추가하기

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

의존성의 종류

  • 세 가지 종류의 의존성을 구분해서 관리하며, 각각의 의존성은 package.json 파일 내의 별도 영역에 존재함
    1. dependencies
      • 현재 프로젝트를 실행하는 데 필수적인 라이브러리들이 포함됨
      • 프로젝트를 npm에 공개하여 다른 사용자가 해당 프로젝트를 설치한다면, dependencies에 들어 있는 라이브러리도 함께 설치됨(전의 의존성)
    2. devDependencies
      • 현재 프로젝트를 개발하고 테스트하는 데 사용되지만, 런타임에는 필요 없는 라이브러리들이 포함됨
        • ex) 테스트 프레임워크
      • 프로젝트를 npm에 공개하여 다른 사용자가 해당 프로젝트를 설치한다면, devDependencies에 포함된 라이브러리들은 제외됨
    3. peerDependencies
      • 런타임에 필요하긴 하지만, 의존성을 직접적으로 관리하지 않는 라이브러리들이 포함됨
        • ex) 플러그인

타입스크립트는 개발 도구일뿐이고 타입 정보는 런타임에 존재하지 않으므로, 타입스크립트와 관련된 라이브러리는 일반적으로 devDependencies에 속함

공통적으로 고려해야할 의존성

  1. 타입스크립트 자체 의존성 고려

    • 팀원들 모두가 항상 동일한 버전을 설치한다는 보장이 없음
    • 프로젝트를 셋업할 때 별도의 단계가 추가됨
    • 따라서 타입스크립트를 devDependencies에 넣는 것이 좋음
    • 대부분의 타입스크립트 IDE와 빌드 도구는 devDependencies를 통해 설치된 타입스크립트의 버전을 인식할 수 있도록 되어 있음 ⭐⭐⭐⭐⭐
      • 또한 커맨드 라인에서 npx 명령어를 사용해 devDependencies를 통해 설치된 타입스크립트 컴파일러를 실행할 수 있음 ⭐

    devDependencies에 포함되어 있다면, npm install을 실행할 때 팀원들 모두 항상 정확한 버전의 타입스크립트를 설치할 수 있음

  2. 타입 의존성(@types)을 고려해야 함

    • DefinitelyTyped(타입스크립트 커뮤니티에서 유지보수하고 있는 자바스크립트 라이브러리의 타입을 정의한 모음)의 타입 정의들을 npm 레지스트리의 @types 스코프에 공개됨

    • 원본 라이브러리 자체가 dependencies에 있더라도 @types 의존성은 devDependencies에 있어야 함

      • ex) 리액트의 타입 선언과 의존성 추가

        npm install react
        npm install @types/react
        

Item 46) 타입 선언과 관련된 세 가지 버전 이해하기

  • 의존성 관리는 개발자에게 매우 힘든 일이므로, 단순히 라이브러리의 전이적 의존성이 호환되는지 깊게 생각하지 않았을 경우가 흔함

세 가지 고려 사항

  1. 라이브러리의 버전
  2. 타입 선언(@types)의 버전
  3. 타입스크립트의 버전
  • 세 가지 버전 중 하나라도 맞지 않으면, 의존성과 상관없어 보이는 곳에서 엉뚱한 오류가 발생할 수 있음

    • 라이브러리 관리의 복잡한 메커니즘을 이해하는 것이 중요함
  • 타입스크립트에서 일반적으로 의존성을 사용하는 방식은 다음과 같음

    npm install react
    # react@16.8.6
    
    npm install @types/react
    # @types/react@16.8.19
    
    • 메이저 버전과 마이너 버전이 일치하지만 패치 버전은 일치하지 않음
    • 타입 선언 자체에도 버그나 누락이 존재할 수 있으며, 앞선 예제의 경우 라이브러리 자체보다 타입 선언에 더 많은 업데이트가 있음

라이브러리와 타입 정보 버전이 별도로 관리되는 방식의 단점

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

    • 라이브러리 업데이트와 관련된 새로운 기능을 사용하려 할 때마다 타입 오류가 발생하여 런타임 오류 발생이 야기됨
    • 보강 기법을 활용하여, 사용하려는 새 함수와 메서드의 타입 정보를 프로젝트 자체에 추가
  2. 라이브러리보다 타입 선언의 버전이 최신인 경우

    • 타입 선언 버전과 라이브러리의 버전 정보가 어긋나는 경우, 타입 체커는 최신 API 기준으로 코드를 검사하지만 런타임에 실제로 쓰이는 것은 과거 버전일 수 있음
    • 라이브러리와 타입 선언의 버전이 맞도록 라이브러리 버전을 올리거나 타입 버전을 내림
  3. 프로젝트에서 사용하는 타입스크립트 버전보다 라이브러리에서 필요로 하는 타입스크립트 버전이 최신인 경우

    • 로대시, 리액트, 람다 같은 유명한 라이브러리의 타입 정보를 더 정확하게 표현하기 위해서 타입 시스템이 개선되고 버전이 올라가므로 타입스크립트 역시 최신 버전을 사용해야 함
    • declare module 선언으로 라이브러리의 타입 정보를 없애던가, 버전을 핸들링해야 함
  4. @types 의존성이 중복되는 경우

    • @types/foo@types/bar에 의존하는 경우를 가정했을 때, @types/bar가 현재 프로젝트와 호환되지 않는 버전의 @types/foo에 의존한다면 npm은 중첩된 폴더에 별도로 해당 버전을 설치하여 문제를 해결하려고 함
      • 전역 네임스페이스에 있는 타입 선언 모듈이라면 대부분 문제가 발생함
      • npm ls @types/foo를 실행하여 어디서 타입 선언 중복이 발생했는지 추적할 수 있음
      • 버전 핸들링을 통해 서로 버전이 호환되게 하는 방식으로 해결

공식적인 권장사항은 라이브러리가 타입스크립트로 작성된 경우만 타입 선언을 라이브러리에 포함하고 자바스크립트로 작성된 라이브러리는 타입 선언을 DefinitelyTyped에 공개하여 커뮤니티에서 유지보수할 수 있게 맡기는 것이 좋음

Item 47) 공개 API에 등장하는 모든 타입을 export 하기

  • 서드파티의 모듈에서 익스포트되지 않은 타입 정보가 필요한 경우가 생김
    • 웬만하면 필요한 타입을 참조하는 방법을 찾을 수 있음
  • 라이브러리 제작자는 프로젝트 초기에 타입 export부터 작성해야 함
    • 만약 함수의 선언에 이미 타입 정보가 있다면 제대로 export되고 있는 것이며, 타입 정보가 없다면 타입을 명시해야 함

예제

interface SecretName {
  first: string
  last: string
}

interface SecretSanta {
  name: SecretName
  gift: string
}

export function getGift(name: SecretName, gift: string): SecretSanta {
  // ...
}
  • 해당 라이브러리 사용자는 SecretName 또는 SecretSanta를 직접 import 할 수 없고, getGiftimport가 가능함

  • 하지만 타입들은 export된 함수 시그니처에 등장하기 때문에 ParameterReturnType 제너릭 타입을 사용해 추출할 수 있음

    type MySanta = ReturnType<typeof getGift> // SecretSanta
    type MyName = Parameters<typeof getGift>[0] // SecretName
    
  • 공개 API 매개변수에 놓이는 순간 타입은 노출되므로 굳이 숨길 필요 없이, 라이브러리 사용자를 위해 명시적으로 export하는 것이 좋음

Referenced

  • 댄 밴더캄, 『이펙티브 타입스크립트』, 인사이트(2021.11.4), 229 ~ 239p