Skip to main content

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​

SituationDescription
Using JS librarieslodash, jquery, etc. are written in JS
Providing types for buildsLibraries publish .js + .d.ts together
Performance optimizationSpeeds 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​

  1. Fork the repository
git clone https://github.com/DefinitelyTyped/DefinitelyTyped
cd DefinitelyTyped
  1. Write type files
types/
└── my-library/
├── index.d.ts # Type definitions
├── tsconfig.json # Type check config
└── my-library-tests.ts # Type tests
  1. 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>;
  1. 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>
  1. 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)
}
}