Module Augmentation은 기존 모듈이나 라이브러리의 타입을 수정하지 않고 확장 하는 TypeScript 기법입니다. 인터페이스의 선언 병합(Declaration Merging)을 활용합니다.

선언 병합(Declaration Merging)

같은 이름의 interface를 여러 번 선언하면 자동으로 합쳐집니다.

TYPESCRIPT
// 첫 번째 선언
interface User {
  name: string;
}

// 두 번째 선언 — 자동으로 합쳐짐
interface User {
  age: number;
}

// 결과: User = { name: string; age: number }
const user: User = { name: '홍길동', age: 25 }; // 두 속성 모두 필요

이 동작은 interface에서만 가능합니다. type은 재선언 불가합니다.

전역 타입 확장

Window 객체 확장

TYPESCRIPT
// types/global.d.ts
declare global {
  interface Window {
    __APP_CONFIG__: {
      apiUrl: string;
      version: string;
    };
    analytics: {
      track(event: string, data?: Record<string, unknown>): void;
    };
  }
}

export {}; // 모듈로 만들기 위해 필요

// 사용
window.__APP_CONFIG__.apiUrl; // string — 타입 안전
window.analytics.track('click', { button: 'submit' });

Process.env 확장

TYPESCRIPT
// types/env.d.ts
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      NODE_ENV: 'development' | 'production' | 'test';
      DATABASE_URL: string;
      API_KEY: string;
      PORT?: string;
    }
  }
}

export {};

// 사용
process.env.NODE_ENV; // 'development' | 'production' | 'test'
process.env.DATABASE_URL; // string

외부 라이브러리 타입 확장

Express의 Request 확장

TYPESCRIPT
// types/express.d.ts
import 'express';

declare module 'express' {
  interface Request {
    user?: {
      id: number;
      role: 'admin' | 'user';
    };
    requestId: string;
  }
}

// 사용
import { Request, Response } from 'express';

function handler(req: Request, res: Response) {
  req.user?.role;  // 'admin' | 'user' | undefined
  req.requestId;   // string
}

Next.js의 라우트 타입 확장

TYPESCRIPT
// types/next.d.ts
import 'next';

declare module 'next' {
  interface NextApiRequest {
    userId?: number;
  }
}

Zustand 스토어 타입 확장

TYPESCRIPT
// types/zustand.d.ts
import 'zustand';

declare module 'zustand' {
  interface StoreApi<T> {
    devtools?: {
      send(action: string, state: T): void;
    };
  }
}

CSS Modules 타입

TYPESCRIPT
// types/css.d.ts

// CSS Modules — 기본적인 타입
declare module '*.module.css' {
  const classes: Record<string, string>;
  export default classes;
}

// SCSS Modules
declare module '*.module.scss' {
  const classes: Record<string, string>;
  export default classes;
}

// SVG를 React 컴포넌트로
declare module '*.svg' {
  import type { FC, SVGProps } from 'react';
  const component: FC<SVGProps<SVGSVGElement>>;
  export default component;
}

네임스페이스 확장

TYPESCRIPT
// lodash에 커스텀 함수 추가
declare module 'lodash' {
  interface LoDashStatic {
    customFunction(value: string): string;
  }
}

// 사용
import _ from 'lodash';
_.customFunction('test'); // OK — 타입은 있지만 런타임 구현은 별도 필요

주의사항

declare module vs declare global

TYPESCRIPT
// 특정 모듈의 타입을 확장할 때
declare module 'express' {
  interface Request { /* ... */ }
}

// 전역 타입을 확장할 때
declare global {
  interface Window { /* ... */ }
}

export {} 필수

파일이 모듈로 취급되어야 declare module이 제대로 동작합니다. 최상위에 importexport가 없으면 export {}를 추가해야 합니다.

TYPESCRIPT
// ❌ 스크립트로 취급됨 — declare module이 동작하지 않을 수 있음
declare module 'express' { /* ... */ }

// ✅ 모듈로 취급됨
declare module 'express' { /* ... */ }
export {};

tsconfig.json에 포함

JSON
{
  "include": [
    "src/**/*",
    "types/**/*.d.ts"  // 커스텀 타입 선언 포함
  ]
}

정리

  • Declaration Merging은 같은 이름의 interface를 합치는 TypeScript 고유 기능이다
  • declare module로 외부 라이브러리의 타입을 확장할 수 있다
  • declare global로 Window, Process 등 전역 타입을 확장할 수 있다
  • 파일을 모듈로 만들기 위해 export {}가 필요할 수 있다
  • Express, Next.js 등의 Request 객체 확장에 자주 사용된다
댓글 로딩 중...