iBetter Books
수정

Ch 02. 선언 파일(.d.ts) 이해하기

.d.ts 파일은 TypeScript에게 타입 정보를 전달하는 파일입니다. 실행 코드는 없고 타입 선언만 있습니다. JavaScript 라이브러리의 타입을 제공하거나, 환경에 존재하는 전역 변수를 TypeScript에 알릴 때 사용합니다.

.d.ts 파일의 구조

.d.ts 파일은 일반 TypeScript 파일과 비슷하지만 모든 선언 앞에 declare 키워드가 붙거나, 구현이 없는 타입 선언만 존재합니다.

// 파일: src/types/api.d.ts// 인터페이스와 타입 별칭은 declare 없이도 됩니다export interface User {  id: number;  name: string;  email: string;}export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";// 함수는 구현 없이 서명만export declare function fetchUser(id: number): Promise<User>;// 클래스는 구현 없이 멤버만export declare class ApiClient {  constructor(baseUrl: string, apiKey: string);  get<T>(path: string): Promise<T>;  post<T>(path: string, body: unknown): Promise<T>;}

declare 키워드와 앰비언트 선언

declare는 "이 값은 어딘가에 존재한다"고 컴파일러에 알리는 키워드입니다. 구현 코드 없이 타입만 선언합니다.

// 파일: src/types/globals.d.ts// 전역 변수 선언 — 런타임에 window.APP_VERSION이 존재한다고 알림declare const APP_VERSION: string;declare const APP_ENV: "development" | "staging" | "production";// 전역 함수 선언declare function log(message: string, level?: "info" | "warn" | "error"): void;// 전역 클래스 선언declare class EventEmitter {  on(event: string, listener: (...args: unknown[]) => void): this;  off(event: string, listener: (...args: unknown[]) => void): this;  emit(event: string, ...args: unknown[]): boolean;}
// 파일: src/app.ts// globals.d.ts가 있으면 이렇게 사용 가능console.log(APP_VERSION);  // 오류 없음log("앱 시작", "info");   // 오류 없음

모듈 선언

.d.ts 파일에서 모듈을 선언하면 TypeScript 타입이 없는 모듈에 타입을 제공할 수 있습니다.

// 파일: src/types/my-library.d.tsdeclare module "my-library" {  export interface Options {    timeout: number;    retries: number;  }  export function connect(url: string, options?: Options): Promise<void>;  export function disconnect(): void;  export class Client {    constructor(url: string);    send(data: string): Promise<void>;    close(): void;  }  export default Client;}

전역 타입 확장

기존 타입에 새 속성을 추가하거나 기존 인터페이스를 확장할 때 선언 병합(declaration merging)을 사용합니다.

// 파일: src/types/express.d.ts// Express의 Request 타입에 user 속성 추가declare namespace Express {  interface Request {    user?: {      id: number;      name: string;      role: string;    };    sessionId?: string;  }}
// 파일: src/types/window.d.ts// window 객체에 커스텀 속성 추가interface Window {  analytics: {    track(event: string, properties?: Record<string, unknown>): void;    identify(userId: string): void;  };  __APP_CONFIG__: {    apiUrl: string;    featureFlags: Record<string, boolean>;  };}

환경별 타입 선언

빌드 환경에 따라 다른 타입이 필요할 때 사용합니다.

// 파일: src/types/env.d.ts// Vite의 import.meta.env에 커스텀 환경변수 추가interface ImportMetaEnv {  readonly VITE_API_URL: string;  readonly VITE_APP_TITLE: string;  readonly VITE_AUTH_TOKEN: string;}interface ImportMeta {  readonly env: ImportMetaEnv;}
// 파일: src/api/client.ts// env.d.ts 덕분에 타입 안전하게 환경변수 접근const apiUrl = import.meta.env.VITE_API_URL; // string으로 추론됨

.d.ts 파일 자동 생성

TypeScript 컴파일러로 소스 코드에서 .d.ts 파일을 자동으로 생성할 수 있습니다. 라이브러리를 배포할 때 사용합니다.

// tsconfig.json{  "compilerOptions": {    "declaration": true,          // .d.ts 생성    "declarationDir": "./dist",   // 저장 위치    "declarationMap": true,       // .d.ts.map 생성 (소스맵)    "emitDeclarationOnly": false  // .js와 함께 생성  }}
// 파일: src/math.ts (소스)export function add(a: number, b: number): number {  return a + b;}export function multiply(a: number, b: number): number {  return a * b;}
// 자동 생성된 dist/math.d.tsexport declare function add(a: number, b: number): number;export declare function multiply(a: number, b: number): number;

.d.ts 파일이 참조되는 방법

TypeScript는 세 가지 방법으로 .d.ts 파일을 찾습니다.

첫 번째는 tsconfig.json의 include나 files 설정입니다. 두 번째는 /// 트리플 슬래시 참조입니다.

// 파일: src/app.ts/// <reference types="node" />          // @types/node 참조/// <reference path="../types/global.d.ts" /> // 특정 파일 참조// 이후 코드에서 참조된 타입 사용 가능process.env.NODE_ENV;  // @types/node의 타입

세 번째는 패키지의 types 또는 typings 필드입니다.

// package.json{  "name": "my-library",  "main": "./dist/index.js",  "types": "./dist/index.d.ts"}

선언 파일은 TypeScript 생태계의 접착제 역할을 합니다. JavaScript 라이브러리를 TypeScript에서 안전하게 사용하고, 런타임 환경에서 제공하는 전역 API에 타입을 부여하고, 프로젝트 전반에 걸쳐 공유되는 타입 정의를 관리하는 핵심 메커니즘입니다.