TL;DR
.d.ts 파일은 코드의 타입 추론을 돕는 타입 선언 파일(Type Declaration File)입니다.
해당 파일을 통해 기존 JavaScript 모듈의 모양을 설명하고, TypeScript에서 타입을 검사할 수 있게 합니다.
들어가며
TypeScript 프로젝트를 작업하다 보면 .d.ts 확장자를 가진 파일들을 자주 보게 됩니다.
node_modules/
@types/
react/
index.d.ts
global.d.ts
node/
index.d.ts
lodash/
lodash.d.ts
이 파일들은 무엇이고, 왜 필요할까요?
.d.ts 파일이란?
Declaration File (선언 파일)
.d.ts 파일은 Declaration File의 약자로, TypeScript의 타입 정보만을 담고 있는 파일입니다.
// math.d.ts - 선언 파일
export function add(a: number, b: number): number;
export function subtract(a: number, b: number): number;
// 실제 구현 코드는 없고, 타입 정보만 있음실제 구현과의 관계
// math.js - 실제 JavaScript 구현
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}// math.d.ts - 타입 선언
export function add(a: number, b: number): number;
export function subtract(a: number, b: number): number;TypeScript 컴파일러는 .js 파일의 구현과 .d.ts 파일의 선언을 매칭하여 타입 체크를 수행합니다.
왜 필요한가?
1. JavaScript 라이브러리에 타입 추가
JavaScript로 작성된 라이브러리를 TypeScript에서 사용할 때 필요합니다.
// lodash는 JavaScript로 작성됨
import _ from 'lodash';
// TypeScript에서 타입 체크를 받으려면?
_.chunk(['a', 'b', 'c', 'd'], 2);
// ^ 어떤 파라미터를 받는지 알 수 있을까?해결책: @types/lodash 패키지
// @types/lodash/index.d.ts
declare module 'lodash' {
export function chunk<T>(array: T[], size: number): T[][];
// ... 수백 개의 함수 선언
}이제 TypeScript가 타입을 알 수 있습니다!
2. 타입 체크와 자동완성
import _ from 'lodash';
// 자동완성 제공
_.ch // chunk, chain 등이 자동완성됨
// 타입 체크
_.chunk(['a', 'b', 'c'], '2');
// ^^^ ❌ Error: Argument of type 'string' is not assignable to parameter of type 'number'3. API 문서 역할
// user.d.ts
interface User {
/** 사용자 고유 ID */
id: number;
/** 사용자 이름 (2-50자) */
name: string;
/** 이메일 주소 */
email: string;
}
/**
* 사용자 정보를 가져옵니다
* @param id 사용자 ID
* @returns 사용자 객체 또는 null
*/
export function getUser(id: number): Promise<User | null>;선언 파일의 위치
1. DefinitelyTyped (@types)
대부분의 유명 JavaScript 라이브러리는 @types 패키지로 타입 정의가 제공됩니다.
# React 타입 설치
npm install --save-dev @types/react
# Node.js 타입 설치
npm install --save-dev @types/node
# Express 타입 설치
npm install --save-dev @types/express2. 라이브러리 자체에 포함
일부 라이브러리는 자체적으로 .d.ts 파일을 포함합니다.
// package.json
{
"name": "my-library",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts" // 타입 선언 파일 위치
}3. 프로젝트 내부 선언
src/
types/
global.d.ts
api.d.ts
models.d.ts
Type Definition이 없는 모듈 다루기
문제 상황
// untyped-library는 타입 정의가 없는 라이브러리
import something from 'untyped-library';
// ^^^^^^^^^^^^^^^^^^
// ❌ Could not find a declaration file for module 'untyped-library'해결 방법 1: 모듈 선언으로 침묵시키기
// src/types/untyped-modules.d.ts
declare module 'untyped-library';이제 에러는 사라지지만, 타입은 any가 됩니다.
import something from 'untyped-library';
// something의 타입은 any해결 방법 2: 직접 타입 정의하기
// src/types/untyped-library.d.ts
declare module 'untyped-library' {
export interface Config {
apiKey: string;
timeout?: number;
}
export function initialize(config: Config): void;
export function getData(): Promise<any>;
const library: {
version: string;
initialize: typeof initialize;
getData: typeof getData;
};
export default library;
}이제 완전한 타입 체크가 가능합니다!
import library, { initialize, getData } from 'untyped-library';
initialize({ apiKey: 'abc123' }); // ✅
initialize({ apiKey: 123 }); // ❌ Error: Type 'number' is not assignable to type 'string'선언 파일 작성 패턴
함수 선언
// 기본 함수
declare function greet(name: string): string;
// 오버로딩
declare function format(value: string): string;
declare function format(value: number): string;
declare function format(value: Date): string;
// 제네릭
declare function identity<T>(value: T): T;클래스 선언
declare class EventEmitter {
constructor();
on(event: string, listener: Function): this;
off(event: string, listener: Function): this;
emit(event: string, ...args: any[]): boolean;
}인터페이스와 타입
// 인터페이스
interface User {
id: number;
name: string;
email: string;
}
// 타입 별칭
type ID = string | number;
// 유니온 타입
type Status = 'pending' | 'success' | 'error';네임스페이스
declare namespace MyLibrary {
interface Config {
debug: boolean;
}
function init(config: Config): void;
namespace Utils {
function formatDate(date: Date): string;
}
}
// 사용
MyLibrary.init({ debug: true });
MyLibrary.Utils.formatDate(new Date());모듈 확장 (Module Augmentation)
기존 모듈에 타입을 추가할 수 있습니다.
// express.d.ts
import 'express';
declare module 'express' {
interface Request {
user?: {
id: number;
name: string;
};
}
}
// 이제 req.user를 사용할 수 있음
app.get('/profile', (req, res) => {
console.log(req.user?.name); // 타입 체크 O
});전역 선언
// global.d.ts
declare global {
interface Window {
myApp: {
version: string;
init(): void;
};
}
const API_URL: string;
}
export {}; // 모듈로 만들기 위한 빈 export
// 사용
window.myApp.init();
console.log(API_URL);실전 예제
예제 1: 외부 CDN 라이브러리
<!-- index.html -->
<script src="https://cdn.example.com/awesome-library.js"></script>// awesome-library.d.ts
declare namespace AwesomeLibrary {
interface Options {
theme: 'light' | 'dark';
language: string;
}
function init(options: Options): void;
function destroy(): void;
}
// 사용
AwesomeLibrary.init({ theme: 'dark', language: 'ko' });예제 2: Node.js 환경 변수
// env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
API_KEY: string;
PORT: string;
}
}
// 사용
const apiUrl = process.env.API_URL; // string 타입
const port = parseInt(process.env.PORT);예제 3: 이미지 import
// images.d.ts
declare module '*.png' {
const value: string;
export default value;
}
declare module '*.jpg' {
const value: string;
export default value;
}
declare module '*.svg' {
import React from 'react';
const SVG: React.FC<React.SVGProps<SVGSVGElement>>;
export default SVG;
}
// 사용
import logo from './logo.png'; // string
import Icon from './icon.svg'; // React Component예제 4: CSS Modules
// css-modules.d.ts
declare module '*.module.css' {
const classes: { [key: string]: string };
export default classes;
}
declare module '*.module.scss' {
const classes: { [key: string]: string };
export default classes;
}
// 사용
import styles from './Button.module.css';
<button className={styles.primary}>Click</button>.d.ts 파일 자동 생성
tsconfig.json 설정
{
"compilerOptions": {
"declaration": true, // .d.ts 파일 생성
"declarationMap": true, // .d.ts.map 파일 생성 (소스맵)
"outDir": "./dist",
"rootDir": "./src"
}
}소스 코드
// src/math.ts
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}자동 생성된 선언 파일
// dist/math.d.ts
export declare function add(a: number, b: number): number;
export declare function subtract(a: number, b: number): number;주의사항
1. 구현 코드 포함 불가
// ❌ 잘못된 예
export function greet(name: string): string {
return `Hello, ${name}`; // 구현 코드는 .d.ts에 들어가면 안 됨
}
// ✅ 올바른 예
export declare function greet(name: string): string;2. 타입만 export
// types.d.ts
export interface User {
id: number;
name: string;
}
export type Status = 'active' | 'inactive';
// 값은 export 불가
export const MAX_USERS = 100; // ❌ Error3. declare 키워드 사용
// 모듈 외부에서 사용 가능하도록
declare const API_URL: string;
declare function fetchData(): Promise<any>;
declare class HttpClient {}결론
핵심 정리
- .d.ts 파일은 타입 정보만 담고 있는 선언 파일
- JavaScript 라이브러리에 TypeScript 타입을 추가
- @types 패키지로 대부분의 유명 라이브러리 커버
- 타입이 없는 모듈은 직접 선언 가능
- tsconfig.json으로 자동 생성 가능
실천 가이드
- npm 패키지 만들 때:
declaration: true설정으로 .d.ts 자동 생성 - 타입 없는 라이브러리 사용 시: 프로젝트 내 선언 파일 작성
- 전역 타입 추가 시:
global.d.ts파일 활용 - 모듈 확장 시: declare module로 기존 타입 확장
TypeScript의 타입 시스템을 최대한 활용하려면 선언 파일을 잘 이해하고 활용하는 것이 중요합니다!
참고 자료
- What is a d.ts file in TypeScript?
- TypeScript 공식 문서: Declaration Files
- DefinitelyTyped - 커뮤니티 타입 정의 저장소