본문으로 건너뛰기

안동민 개발노트

본문 시작

DefinitelyTyped와 @types

DefinitelyTyped에서 제공하는 @types 패키지를 설치·선택하고 자체 타입 내장 여부와 types·typeRoots 설정을 구분합니다.

타입스크립트로 자바스크립트 라이브러리를 개발하거나, 기존 라이브러리를 타입스크립트 프로젝트에서 사용할 때 핵심은 타입 정의 파일(.d.ts)입니다.

문제는 라이브러리 수가 너무 많아 모든 .d.ts를 개발자가 직접 작성하는 것이 비효율적이고 사실상 불가능에 가깝다는 점입니다.

이러한 문제를 해결하기 위해 등장한 것이 바로 DefinitelyTyped 프로젝트와 npm의 @types 스코프 패키지입니다.

DefinitelyTyped와 @types 배포 흐름

타입스크립트로 자바스크립트 라이브러리를 개발하거나, 기존 라이브러리를 타입스크립트 프로젝트에서 사용할 때 핵심은 타입 정의 파일(.d.ts)입니다. 문제는 라이브러리 수가 너무 많아 모든 .d.ts를 개발자가 직접 작성하는 것이 비효율적이고 사실상 불가능에 가깝다는 점입니다.

  1. 1
    DefinitelyTyped 프로젝트 개념

    DefinitelyTyped는 수많은 자바스크립트 라이브러리에 대한 고품질의 타입 정의 파일을 제공하는 오픈 소스 프로젝트입니다. 타입 정의 파일(.d.ts)

  2. 2
    @types 스코프 패키지

    DefinitelyTyped에 기여된 모든 타입 정의 파일은 자동으로 npm의 @types 스코프로 패키징되어 배포됩니다. DefinitelyTyped

  3. 3
    @types 패키지의 장점

    자동 완성 및 타입 검사: IDE에서 라이브러리 사용 시 정확한 자동 완성( .)과 함수 시그니처 힌트를 제공하여 개발 생산성을 크게 높입니다. @types 스코프 패키지

  4. 4
    DefinitelyTyped와 @types 배포 흐름 기준

    정확성 타입스크립트로 자바스크립트 라이브러리를 개발하거나 기존 라이브러리를 가져올 때는 타입 정의 제공 여부와 유지 상태를 먼저 확인합니다. 비용 DefinitelyTyped는 외부 라이브러리 타입 정의를 모아 관리합니다. 확장성 @types 패키지는 런타임 코드를 포함하지 않고 npm 스코프 안에서 선언 파일만 배포해 기존 JS 라이브러리에 타입 경계를 붙입니다. 예외 설치된 타입 정의가 실제 런타임 API와 맞는지 확인합니다.

  5. 5
    정확성 타입스크립트

    자바스크립트 라이브러리를 개발하거나 기존 라이브러리를 가져올 때는 타입 정의 제공 여부와 유지 상태를 먼저 확인합니다.

  6. 6
    비용 DefinitelyTyped

    외부 라이브러리 타입 정의를 모아 관리합니다.

  7. 7
    확장성 @types 패키지

    런타임 코드를 포함하지 않고 npm 스코프 안에서 선언 파일만 배포해 기존 JS 라이브러리에 타입 경계를 붙입니다.

  8. 8
    예외 설치된 타입 정의

    실제 런타임 API와 맞는지 확인합니다.


DefinitelyTyped 프로젝트란?

DefinitelyTyped는 수많은 자바스크립트 라이브러리에 대한 고품질의 타입 정의 파일을 제공하는 오픈 소스 프로젝트입니다.

전 세계의 개발자들이 자발적으로 참여하여 각 라이브러리의 .d.ts 파일을 작성하고 유지보수하며, 이를 통해 타입스크립트 사용자들이 자바스크립트 생태계를 타입 안전하게 활용할 수 있도록 돕습니다.

DefinitelyTyped는 GitHub 저장소(https://github.com/DefinitelyTyped/DefinitelyTyped)에서 관리되며, 각 라이브러리별 타입 정의 파일이 별도 디렉토리에 존재합니다.

예를 들어 Lodash 라이브러리 타입 정의는 DefinitelyTyped/types/lodash 경로에 있습니다.


@types 스코프 패키지

DefinitelyTyped에 기여된 모든 타입 정의 파일은 자동으로 npm의 @types 스코프로 패키징되어 배포됩니다.

즉, lodash 라이브러리에 대한 타입 정의는 npm install @types/lodash 명령어로 설치할 수 있게 됩니다.

@types 패키지들은 실제 자바스크립트 코드를 포함하지 않고, 오직 해당 라이브러리의 타입 정의(.d.ts 파일)만을 포함합니다.

타입스크립트 컴파일러는 기본적으로 node_modules/@types 디렉토리를 자동으로 검색하여 필요한 타입 정의 파일을 찾습니다.

사용 예시

lodash 라이브러리를 타입스크립트 프로젝트에서 사용하려고 할 때:

lodash 라이브러리 설치
npm install lodash
lodash에 대한 타입 정의 설치
npm install --save-dev @types/lodash

--save-dev 플래그는 개발 의존성(devDependencies)으로 설치하여, 실제 프로덕션 환경에는 포함되지 않음을 나타냅니다.

타입 정의 파일은 컴파일 시에만 필요하기 때문입니다.

이제 타입스크립트 코드에서 lodash를 가져와 사용하면, 타입스크립트 컴파일러는 node_modules/@types/lodash 경로의 .d.ts 파일을 참조하여 _ 객체의 모든 메서드와 속성에 대한 정확한 타입 정보를 제공합니다.

import _ from 'lodash'; // 'lodash' 모듈을 가져옴 (CommonJS 모듈이므로 esModuleInterop: true 권장)

const numbers = [1, 2, 3, 4, 5];
const sum = _.sum(numbers); // sum은 number 타입으로 추론됩니다. (정확한 매개변수 힌트 제공)
console.log(sum); // 15

const shuffled = _.shuffle(numbers); // shuffled는 number[] 타입으로 추론됩니다.
console.log(shuffled);

// _.sortBy(numbers, 'invalidKey'); // Error: 'string' 형식은 'lodash.ValueIteratee<number>' 형식에 할당될 수 없습니다.
                                  // 타입 정의 덕분에 잘못된 인자 사용 시 오류를 즉시 감지합니다.

@types 패키지의 장점

  • 자동 완성 및 타입 검사: IDE에서 라이브러리 사용 시 정확한 자동 완성(_.)과 함수 시그니처 힌트를 제공하여 개발 생산성을 크게 높입니다. 잘못된 인자 전달이나 존재하지 않는 메서드 호출 등의 오류를 컴파일 시점에 즉시 잡아줍니다.
  • 쉬운 통합: npm install 한 줄로 수많은 자바스크립트 라이브러리를 타입스크립트 프로젝트에 타입 안전하게 통합할 수 있습니다.
  • 커뮤니티 주도: 전 세계 개발자들의 기여로 최신 라이브러리 버전에도 빠르게 대응하고, 버그 수정 및 개선이 활발하게 이루어집니다.
  • 명확한 버전 관리: @types 패키지는 해당 라이브러리의 특정 버전(또는 범위)에 맞는 타입 정의를 제공하므로, 라이브러리 버전과 타입 정의 버전 간의 호환성을 관리하기 용이합니다.

@types 패키지가 없을 때

만약 특정 자바스크립트 라이브러리에 대한 @types 패키지가 존재하지 않는다면, 다음 두 가지 방법 중 하나를 선택할 수 있습니다.

직접 .d.ts 파일 작성: 해당 라이브러리의 외부 API를 파악하여 직접 앰비언트 모듈 선언(.d.ts 파일)을 작성합니다 (8장 3절 참조).

이 방법은 라이브러리의 규모가 작거나, 사용하려는 부분이 제한적일 때 유용합니다.

my-lib-declaration.d.ts
declare module 'my-custom-js-lib' {
  export function doSomething(param: string): number;
}
app.ts
import { doSomething } from 'my-custom-js-lib';
doSomething('hello');

any 타입으로 사용 (최후의 수단): 정의된 타입이 없어 타입 안정성을 포기하고 any 타입으로 라이브러리를 사용하는 방법입니다.

app.ts
import SomeUntypedLib from 'some-untyped-lib'; // 타입 정보가 없으므로 SomeUntypedLib는 any 타입
const lib = new SomeUntypedLib();
lib.doSomethingElse(123); // 타입 검사 없이 허용

이 방법은 타입스크립트 사용의 이점을 상실하므로 가능한 한 피해야 합니다.

아래 다이어그램은 새 라이브러리를 도입할 때 타입 정의를 찾는 우선순위와, 없을 때 선택지를 비교합니다.

라이브러리 타입은 가까운 곳부터 확인합니다

새 패키지를 설치할 때는 자체 타입, @types , 직접 선언, 임시 any 순서로 타입 안정성을 최대한 유지합니다.

  1. 1
    패키지 자체 타입

    package.json 의 types 가 가장 신뢰하기 쉽습니다.

  2. 2
    @types 설치

    DefinitelyTyped에 있으면 @types/lodash 같은 개발 의존성으로 추가합니다.

  3. 3
    작은 선언 작성

    필요한 함수와 값만 declare module 로 좁게 적어 타입 표면을 만듭니다.

  4. 4
    임시 완화

    any 는 마이그레이션 중 외부 값 경계에만 두고, 반복 호출 지점은 선언 파일이나 unknown guard로 좁힙니다.

  5. 5
    TypeScript는 node_modules/@types 를 봅니다

    검색 위치 기본 설정에서는 별도 지정 없이도 설치된 전역 타입 패키지를 자동으로 포함합니다.

  6. 6
    types 와 typeRoots

    제어 옵션 충돌이 있거나 커스텀 선언 경로가 필요하면 포함할 타입 범위를 명시합니다.


라이브러리가 자체적으로 타입 정의를 제공하는 경우

타입 정의는 자체 제공, @types, 직접 선언 순서로 확보한다

외부 라이브러리 타입 오류가 나면 먼저 패키지가 타입을 포함하는지 보고, 없으면 DefinitelyTyped를 찾는다. 마지막에만 필요한 표면을 직접 선언하고 any는 격리한다.

  1. 1
    package.json

    `types` 또는 `typings` 필드가 있으면 자체 타입을 쓴다.

  2. 2
    @types 검색

    DefinitelyTyped 패키지와 라이브러리 버전을 맞춘다.

  3. 3
    직접 .d.ts

    실제 사용하는 API만 좁게 선언한다.

  4. 4
    검색 범위 설정

    `types`와 `typeRoots`로 전역 타입 포함 범위를 관리한다.

"types": "dist/index.d.ts"

일부 최신 자바스크립트 라이브러리(특히 타입스크립트로 작성된 라이브러리)는 package.json 파일의 types (또는 typings) 필드를 통해 자체적으로 타입 정의 파일을 포함하여 배포합니다.

my-typescript-lib/package.json
{
  "name": "my-typescript-lib",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts"
}

기존 package.json에 적용할 때는 maintypes 키를 병합하고 다른 스크립트와 메타데이터는 유지합니다.

이러한 라이브러리들은 @types 패키지를 별도로 설치할 필요가 없습니다.

npm으로 라이브러리를 설치하면 타입스크립트 컴파일러가 package.jsontypes 필드를 참조하여 자동으로 타입 정의를 찾습니다.

정리
  • 라이브러리가 자체 types 필드를 가짐: @types 설치 필요 없음.
  • 라이브러리가 자체 types 필드를 가지지 않음: @types/<라이브러리명> 설치 시도.
  • @types에도 없음: 직접 .d.ts 파일 작성 또는 any 사용.

tsconfig.jsontypestypeRoots 옵션

tsconfig.json 파일의 compilerOptions에서 typestypeRoots 옵션을 사용하여 타입 정의 파일의 검색 방식을 더 세밀하게 제어할 수 있습니다.

  • typeRoots: node_modules/@types와 같이 타입 정의 파일을 검색할 루트 디렉토리 목록을 지정합니다. 기본적으로 node_modules/@types가 포함되어 있으므로, 대부분의 경우 명시할 필요는 없습니다. 하지만 @types가 아닌 특정 커스텀 타입 정의 경로를 추가하고 싶을 때 사용합니다.
    "compilerOptions": {
      "typeRoots": ["./node_modules/@types", "./custom-types"] // custom-types 디렉토리도 검색
    }
  • types: 컴파일러가 전역 타입 선언으로 포함할 @types 패키지 목록을 지정합니다. 이 옵션이 없으면 typeRoots에 있는 모든 @types 패키지가 자동으로 포함되지만, types를 명시하면 여기에 나열된 패키지만 포함됩니다.
    "compilerOptions": {
      "types": ["node", "jest"] // 오직 @types/node와 @types/jest만 포함하고 싶을 때
    }
    이는 번들링 크기를 줄이거나, 불필요한 전역 타입 충돌을 피하고 싶을 때 유용합니다.

아래 다이어그램은 typestypeRoots가 전역 타입 선언의 포함 범위를 어떻게 좁히는지 정리합니다.

전역 타입 선언 범위

TypeScript는 기본적으로 node_modules/@types 를 자동 탐색하지만, 테스트 러너나 런타임 전역 타입이 겹치면 types 와 typeRoots 로 범위를 명시합니다.

  1. 모든 @types 자동 포함

    기본값 가까운 상위 디렉터리의 @types 패키지를 전역 선언 후보로 읽습니다.

  2. 패키지 이름 화이트리스트

    types "types": ["node", "jest"] 나열한 전역 타입만 포함하여 DOM, Node, Jest 같은 전역 충돌을 제어합니다.

  3. 탐색 루트 디렉터리 지정

    typeRoots "typeRoots": ["./types", "./node_modules/@types"] 사내 선언 파일이나 커스텀 앰비언트 모듈 위치를 명시적으로 추가합니다.

  4. 전역 선언 포함

    types 프로젝트 전체에 자동 주입되는 전역 이름의 목록을 좁힙니다. typeRoots 전역 타입 패키지를 어느 디렉터리에서 찾을지 바꿉니다.

  5. 모듈 import 해석

    import 코드에서 직접 가져온 모듈 타입은 별도의 모듈 해석 규칙을 따릅니다. 전역 타입 선언 한계 전역 타입 제한과 런타임 패키지 설치 여부는 서로 다른 문제입니다.

@types를 설치할지, 자체 타입을 믿을지, 직접 선언을 둘지 결정할 때는 패키지의 현재 배포 형태와 프로젝트의 전역 타입 범위를 같이 확인해야 합니다.

타입 패키지는 가장 가까운 출처부터 믿는다

새 라이브러리를 쓸 때는 자체 타입, DefinitelyTyped, 직접 선언 순서로 좁히고 전역 타입 범위를 함께 통제합니다.

  1. own
    자체 타입

    own 패키지의 types 또는 exports 가 타입 파일을 제공하면 별도 설치를 피합니다.

  2. @types
    커뮤니티 타입

    @types 런타임 패키지와 @types 버전이 맞는지 보고 오래된 선언인지 확인합니다.

  3. local
    직접 선언

    local 공식 타입도 커뮤니티 타입도 없다면 사용 중인 표면만 작은 .d.ts 로 점검합니다.

  4. 범위
    포함 범위

    범위 types 옵션은 필요한 전역 타입만 넣어 테스트 도구 간 충돌을 줄입니다.


DefinitelyTyped와 @types는 타입스크립트 생태계의 핵심이며, 자바스크립트와 타입스크립트 간 호환성을 보장하는 중요한 메커니즘입니다.

이를 통해 수많은 기존 자바스크립트 라이브러리를 타입 안전하게 활용할 수 있어, 타입스크립트 도입 장벽을 낮추고 개발 생산성을 높일 수 있습니다.

새 라이브러리를 사용할 때는 @types 패키지 존재 여부를 먼저 확인하는 습관을 들이는 것이 좋습니다.


다음 다이어그램은 DefinitelyTyped와 @types 패키지를 타입 제공 방식 기준으로 정리한 표입니다.

타입 정의 출처와 버전 점검

라이브러리가 자체 타입을 제공하는지, @types 패키지가 필요한지, tsconfig가 어떤 타입을 포함하는지 순서대로 확인합니다.

  1. 1
    패키지 내장

    자체 타입 package.json의 types 또는 exports가 선언 파일을 제공하면 별도 패키지가 필요 없습니다. types field

  2. 2
    외부 선언

    @types 패키지 런타임 패키지에 타입이 없을 때 DefinitelyTyped 기반 패키지를 설치합니다. @types/lodash

  3. 3
    API 일치

    버전 확인 타입 패키지가 실제 라이브러리 버전의 API와 맞는지 확인합니다. version match

  4. 4
    포함 제어

    types 옵션 전역 타입이 너무 많이 섞이면 tsconfig의 types로 필요한 항목만 제한합니다. types: []

아래 다이어그램은 DefinitelyTyped 프로젝트와 @types 스코프 패키지가 타입 해석과 빌드 결과에 주는 영향을 정리합니다.

모듈 타입 탐색과 전역 포함을 나눠 본다

같은 @types 라도 import의 타입을 찾는 일과 전역 이름을 넣는 일은 진단 경로가 다르다.

  1. 모듈의 export 모양을 찾는다

    import "foo" 1 PKG exports types 조건· types 진입점 2 @TYPES @types/foo 3 LOCAL 직접 .d.ts 로 specifier·export 복제

  2. @TYPES
  3. 보이는 타입 패키지의 범위를 고른다

    전역 이름 DEFAULT 가시적 @types 루트 ROOTS typeRoots 가 후보 루트를 교체 NAMES types 가 포함 이름을 제한

  4. 모듈 선언을 못 찾음

    404 --traceResolution 으로 specifier·패키지 진입점·설치 위치를 확인한다.

  5. 전역 이름이 사라짐

    GLOBAL types 목록과 typeRoots 가 필요한 패키지를 제외했는지 본다.

  6. 식별자가 중복됨

    DUP 전역 선언 패키지· lib ·중복 버전이 같은 이름을 합치는지 추적한다.

  7. 타입은 맞고 실행이 깨짐

    RUN 라이브러리 버전과 선언의 export 모양을 맞춘다. 선언은 코드를 설치하지 않는다.