TypeScript Declaration Files

Author ambient declaration files, bundle package typings, augment global interfaces, and publish typed libraries for the community.

TL;DR

  1. Author .d.ts files to describe types for external JavaScript modules.
  2. Define ambient global variables using the root declare keyword modifier.
  3. Specify package types mapping inside package.json using the types field.

Ambient Declarations

    Ambient variable declaration

    Declare global variables defined at runtime.

    declare const __API_VERSION__: string;
    declare const __DEBUG__: boolean;
    Ambient function signature

    Type JavaScript functions without bodies.

    declare function notifyUser(
      message: string,
      timeoutMs?: number
    ): void;
    Ambient class declaration

    Define class methods and property signatures.

    declare class ClientEngine {
      constructor(apiKey: string);
      connect(): Promise<boolean>;
    }
    Global scope augmentation

    Extend global window or node namespaces.

    declare global {
      interface Window {
        appConfig?: { env: string };
      }
    }

Module Declarations

    External module stub

    Supply types for untyped third-party packages.

    declare module "legacy-logger" {
      export function log(msg: string): void;
      export const level: number;
    }
    Module augmentation

    Add custom properties to library interfaces.

    import "express";
    declare module "express" {
      interface Request {
        userId?: string;
      }
    }
    Wildcard asset declaration

    Permit importing SVG and image assets.

    declare module "*.svg" {
      const content: string;
      export default content;
    }
    CSS module declaration

    Type CSS module classname dictionaries.

    declare module "*.module.css" {
      const classes: Record<string, string>;
      export default classes;
    }

Package and NPM Typings

    Basic package types field

    Direct compiler to main declaration file.

    {
      "name": "my-lib",
      "main": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
    Exports map conditional types

    Map types for ESM and CJS consumers.

    {
      "exports": {
        ".": {
          "import": "./dist/esm/index.js",
          "types": "./dist/index.d.ts"
        }
      }
    }
    Compiler declaration emit

    Instruct tsc to emit d.ts and source maps.

    {
      "compilerOptions": {
        "declaration": true,
        "declarationMap": true,
        "outDir": "./dist"
      }
    }

DefinitelyTyped and Testing

    DefinitelyTyped file layout

    Standard structure for Community @types packages.

    // types/awesome-lib/
    //   index.d.ts
    //   package.json
    //   tsconfig.json
    //   awesome-lib-tests.ts
    Consumer type test

    Assert expected types in test suites.

    import * as lib from "awesome-lib";
    // Type checking assertions:
    const res: string = lib.format("test");
    Avoid any in declarations

    Use generic parameters rather than any.

    // Prefer generics over any:
    declare function parseData<T>(
      raw: string
    ): T;
    Export equals for CommonJS

    Model legacy CommonJS single export modules.

    declare function legacyRunner(): void;
    export = legacyRunner;

Tips

  1. Generate .d.ts files automatically during build pipelines by setting declaration and declarationMap to true in compiler options.
  2. Prefer unknown and generic constraints in ambient function definitions instead of resorting to loose un-typed any annotations.

Warnings

  1. Never import runtime JavaScript logic inside ambient .d.ts files, as ambient declarations must contain purely erased type signatures.
  2. Inaccurate ambient declarations worse than missing types; discrepancies between declare signatures and runtime behavior trigger subtle consumer errors.

In Practice

FAQ