Module Augmentation — 기존 라이브러리 타입 확장하기
Module Augmentation은 기존 모듈이나 라이브러리의 타입을 수정하지 않고 확장 하는 TypeScript 기법입니다. 인터페이스의 선언 병합(Declaration Merging)을 활용합니다.
선언 병합(Declaration Merging)
같은 이름의 interface를 여러 번 선언하면 자동으로 합쳐집니다.
// 첫 번째 선언
interface User {
name: string;
}
// 두 번째 선언 — 자동으로 합쳐짐
interface User {
age: number;
}
// 결과: User = { name: string; age: number }
const user: User = { name: '홍길동', age: 25 }; // 두 속성 모두 필요
이 동작은 interface에서만 가능합니다. type은 재선언 불가합니다.
전역 타입 확장
Window 객체 확장
// 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 확장
// 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 확장
// 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의 라우트 타입 확장
// types/next.d.ts
import 'next';
declare module 'next' {
interface NextApiRequest {
userId?: number;
}
}
Zustand 스토어 타입 확장
// types/zustand.d.ts
import 'zustand';
declare module 'zustand' {
interface StoreApi<T> {
devtools?: {
send(action: string, state: T): void;
};
}
}
CSS Modules 타입
// 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;
}
네임스페이스 확장
// lodash에 커스텀 함수 추가
declare module 'lodash' {
interface LoDashStatic {
customFunction(value: string): string;
}
}
// 사용
import _ from 'lodash';
_.customFunction('test'); // OK — 타입은 있지만 런타임 구현은 별도 필요
주의사항
declare module vs declare global
// 특정 모듈의 타입을 확장할 때
declare module 'express' {
interface Request { /* ... */ }
}
// 전역 타입을 확장할 때
declare global {
interface Window { /* ... */ }
}
export {} 필수
파일이 모듈로 취급되어야 declare module이 제대로 동작합니다. 최상위에 import나 export가 없으면 export {}를 추가해야 합니다.
// ❌ 스크립트로 취급됨 — declare module이 동작하지 않을 수 있음
declare module 'express' { /* ... */ }
// ✅ 모듈로 취급됨
declare module 'express' { /* ... */ }
export {};
tsconfig.json에 포함
{
"include": [
"src/**/*",
"types/**/*.d.ts" // 커스텀 타입 선언 포함
]
}
정리
- Declaration Merging은 같은 이름의 interface를 합치는 TypeScript 고유 기능이다
declare module로 외부 라이브러리의 타입을 확장할 수 있다declare global로 Window, Process 등 전역 타입을 확장할 수 있다- 파일을 모듈로 만들기 위해
export {}가 필요할 수 있다 - Express, Next.js 등의 Request 객체 확장에 자주 사용된다
댓글 로딩 중...