Skip to main content
Advertisement

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)
}
}
Advertisement