Declaration Files (.d.ts)

Difficulty: Advanced

Question

What are declaration files (.d.ts) in TypeScript? How do they enable TypeScript to work with JavaScript libraries?

Answer

Declaration files (.d.ts) contain only type information with no runtime code. They describe the shape of JavaScript libraries so TypeScript can type-check code that uses them.

Sources of declarations: 1. Generated by tsc (declaration: true in tsconfig) - published alongside compiled JS 2. DefinitelyTyped (@types/xxx packages) - community-maintained types for JS libraries 3. Hand-written ambient declarations for custom or untyped code

Declaration files use 'declare' keyword for ambient declarations - telling TypeScript that something exists at runtime without providing implementation. They are essential for the TypeScript ecosystem because they allow gradual adoption without rewriting libraries.

Code examples

Writing Declaration Files

// math-lib.d.ts - types for a JavaScript library
declare module 'math-lib' {
  export function add(a: number, b: number): number;
  export function subtract(a: number, b: number): number;
  export function multiply(a: number, b: number): number;
  
  export interface MathOptions {
    precision: number;
    rounding: 'ceil' | 'floor' | 'round';
  }
  
  export class Calculator {
    constructor(options?: Partial<MathOptions>);
    compute(expression: string): number;
    reset(): void;
  }
  
  export default Calculator;
}

// Now TypeScript understands imports from 'math-lib'
import Calculator, { add, MathOptions } from 'math-lib';
const result = add(1, 2);           // number
const calc = new Calculator();      // Calculator
calc.compute('2 + 2');              // number

declare module tells TypeScript what types a module exports. This lets you use JavaScript libraries with full type safety and autocompletion.

Ambient Declarations & Global Types

// globals.d.ts - global type declarations

// Declare global variables (e.g., injected by script tags)
declare const API_BASE_URL: string;
declare const APP_VERSION: string;
declare function sendAnalytics(event: string, data?: object): void;

// Extend existing global interfaces
declare global {
  interface Window {
    dataLayer: Record<string, unknown>[];
    __APP_CONFIG__: {
      apiUrl: string;
      environment: 'dev' | 'staging' | 'production';
    };
  }

  // Add custom properties to process.env
  namespace NodeJS {
    interface ProcessEnv {
      NODE_ENV: 'development' | 'production' | 'test';
      DATABASE_URL: string;
      JWT_SECRET: string;
      PORT?: string;
    }
  }
}

export {}; // Makes this a module (required for declare global)

// Usage is now type-safe
window.__APP_CONFIG__.apiUrl;       // string
process.env.DATABASE_URL;           // string
process.env.RANDOM_VAR;             // Error if not declared

declare global extends existing global types. This is commonly used to type environment variables and window properties injected by build tools.

Using @types and DefinitelyTyped

// Installing types for a JS library
// npm install --save-dev @types/lodash
// npm install --save-dev @types/express
// npm install --save-dev @types/node

// TypeScript auto-discovers @types packages in node_modules
import _ from 'lodash';
_.chunk([1, 2, 3, 4], 2);  // number[][] - fully typed!

// For libraries without @types, create a shim:
// untyped-lib.d.ts
declare module 'untyped-lib' {
  const lib: any;
  export default lib;
}
// Minimal: allows import without errors, but no type safety

// Better: add specific types incrementally
declare module 'untyped-lib' {
  export function doSomething(input: string): Promise<void>;
  export function getData(): Record<string, unknown>;
}

// tsconfig.json settings for declarations
// {
//   "compilerOptions": {
//     "declaration": true,        // Generate .d.ts files
//     "declarationDir": "./types", // Output directory
//     "typeRoots": ["./types", "./node_modules/@types"]
//   }
// }

DefinitelyTyped (@types/*) is the largest collection of community-maintained type declarations. For untyped libraries, write a minimal .d.ts shim and improve it incrementally.

Key points

Concepts covered

Declaration Files, .d.ts, DefinitelyTyped, @types, Ambient Declarations, Triple-Slash Directives