8.2 Declaration Files (.d.ts) — The World of Type Definitions
What are Declaration Files?
.d.ts files contain type information only — no implementation code. They tell the TypeScript compiler "this value has this type."
// math.d.ts — Declaration file (no implementation)
export declare function add(a: number, b: number): number;
export declare function subtract(a: number, b: number): number;
export declare const PI: number;
// math.js — Actual implementation (no types)
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }
export const PI = 3.14159;
TypeScript uses the .js + .d.ts pair to safely use JavaScript libraries with full type support.
Why Declaration Files Are Needed
| Situation | Description |
|---|---|
| Using JS libraries | lodash, jquery, etc. are written in JS |
| Providing types for builds | Libraries publish .js + .d.ts together |
| Performance optimization | Speeds up type checking with skipLibCheck |
Auto-generating .d.ts with tsc
// tsconfig.json
{
"compilerOptions": {
"declaration": true, // Generate .d.ts files
"declarationMap": true, // Generate .d.ts.map source maps
"declarationDir": "./types", // Output directory for .d.ts (optional)
"emitDeclarationOnly": true // Generate only .d.ts (when bundler handles JS)
}
}
// src/utils.ts
export function greet(name: string): string {
return `Hello, ${name}!`;
}
export interface Config {
timeout: number;
retries: number;
}
Compilation output:
// dist/utils.d.ts (auto-generated)
export declare function greet(name: string): string;
export interface Config {
timeout: number;
retries: number;
}
@types/ Packages — DefinitelyTyped
For JavaScript libraries without built-in TypeScript types, install separate @types/ packages.
npm install --save-dev @types/node # Node.js types
npm install --save-dev @types/express # Express types
npm install --save-dev @types/lodash # lodash types
npm install --save-dev @types/jest # Jest types
How to Check if Types Are Needed
import express from 'express';
// Red underline = need to install @types/express
// No underline = library has built-in types (e.g., axios)
Libraries with Built-in Types vs @types Required
// Built-in types (no separate install)
import axios from 'axios'; // axios: includes "types" in package.json
import { z } from 'zod'; // zod: written in TypeScript
import { PrismaClient } from '@prisma/client';
// @types required
import express from 'express'; // needs @types/express
import _ from 'lodash'; // needs @types/lodash
import fs from 'fs'; // included in @types/node
Writing Declaration Files Manually
Function Declarations
// globals.d.ts
declare function fetchUser(id: number): Promise<User>;
declare function formatDate(date: Date, format: string): string;
Variable/Constant Declarations
declare const API_URL: string;
declare let currentUser: User | null;
declare var __DEV__: boolean;
Interface and Type Declarations
// types.d.ts
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
type Nullable<T> = T | null;
type Optional<T> = T | undefined;
Class Declarations
declare class EventEmitter {
on(event: string, listener: (...args: any[]) => void): this;
off(event: string, listener: (...args: any[]) => void): this;
emit(event: string, ...args: any[]): boolean;
}
Module Declaration Files
Used to add types to existing JavaScript libraries.
// legacy-lib.d.ts
declare module 'legacy-lib' {
export function initialize(config: LegacyConfig): void;
export function process(data: string): string;
interface LegacyConfig {
apiKey: string;
timeout?: number;
debug?: boolean;
}
}
// Usage
import { initialize, process } from 'legacy-lib';
initialize({ apiKey: 'abc123' }); // ✅ Type safe
Real-World Example: Providing .d.ts for a Library
my-utils/
├── src/
│ ├── string.ts
│ └── array.ts
├── dist/
│ ├── string.js
│ ├── string.d.ts
│ ├── array.js
│ └── array.d.ts
├── package.json
└── tsconfig.json
// package.json
{
"name": "my-utils",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts", // Types entry point
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts" // ESM types entry point
}
}
}
// src/string.ts
/**
* Capitalizes the first letter of a string
*/
export function capitalize(str: string): string {
if (!str) return str;
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* Converts a string to camelCase
*/
export function toCamelCase(str: string): string {
return str
.split(/[-_\s]+/)
.map((word, i) => i === 0 ? word.toLowerCase() : capitalize(word))
.join('');
}
After compilation:
// dist/string.d.ts (auto-generated)
/**
* Capitalizes the first letter of a string
*/
export declare function capitalize(str: string): string;
/**
* Converts a string to camelCase
*/
export declare function toCamelCase(str: string): string;
JSDoc comments are preserved in .d.ts files. Hovering in the IDE will show the documentation.
Contributing to DefinitelyTyped
DefinitelyTyped is a community-maintained repository of TypeScript type definitions.
Contribution Steps
- Fork the repository
git clone https://github.com/DefinitelyTyped/DefinitelyTyped
cd DefinitelyTyped
- Write type files
types/
└── my-library/
├── index.d.ts # Type definitions
├── tsconfig.json # Type check config
└── my-library-tests.ts # Type tests
- index.d.ts writing rules
// types/my-library/index.d.ts
// Header comment (required)
// Type definitions for my-library 1.0
// Project: https://github.com/author/my-library
// Definitions by: Your Name <https://github.com/yourname>
export interface Options {
timeout?: number;
retries?: number;
}
export function request(url: string, options?: Options): Promise<Response>;
- Write type tests
// my-library-tests.ts
import { request, Options } from 'my-library';
// Verify types with $ExpectType comments
const opts: Options = { timeout: 5000 };
const result = request('https://api.example.com', opts);
// $ExpectType Promise<Response>
- Submit PR → After review, published as
@types/my-library
Pro Tips
1. Specify type search paths with typeRoots
{
"compilerOptions": {
"typeRoots": [
"./node_modules/@types", // default
"./types" // custom types directory
]
}
}
2. Restrict @types packages with types
{
"compilerOptions": {
"types": ["node", "jest"] // Only include these as global types
}
}
3. Inline type declaration override
// src/types/express/index.d.ts
import 'express';
declare module 'express' {
interface Request {
user?: AuthUser; // Extend Express's Request type
}
}
4. Performance optimization with skipLibCheck: true
{
"compilerOptions": {
"skipLibCheck": true // Skip type checking .d.ts files (faster builds)
}
}