Learning Guides
Menu

Declaration Files Deep Dive

3 min readEffective TypeScript

Declaration Files Deep Dive

Declaration files (.d.ts) describe the shape of JavaScript code. They’re critical for sharing typed APIs and integrating untyped libraries.

Item 108: Use declare module for External Packages

TYPESCRIPT
// types/some-lib.d.ts
declare module "some-lib" {
  export function format(input: string): string;
}

Why it matters: without a declare module, TypeScript treats the package as any. You lose autocomplete and type safety across your entire codebase.

This tells TypeScript how to type the external package.

Item 109: Use declare global Only for True Globals

TYPESCRIPT
export {};
 
declare global {
  interface Window {
    analytics: { track(event: string): void };
  }
}

Why it matters: global declarations leak everywhere. Keeping them rare prevents surprising name collisions and hard‑to‑trace type errors.

Item 110: Model Functions, Objects, and Classes Explicitly

TYPESCRIPT
declare function parse(input: string): number;
 
declare const version: string;
 
declare class Client {
  constructor(apiKey: string);
  request(path: string): Promise<unknown>;
}

Why it matters: precise declarations keep consumers safe and reduce misuse of APIs. Vague types turn your library into any again.

Item 111: Use Overloads for API Variants

TYPESCRIPT
declare function format(input: string): string;
declare function format(input: number): string;
declare function format(input: string | number): string;

Why it matters: overloads preserve the best return type for each input, improving DX and preventing unnecessary type assertions by users.

Item 112: Support CommonJS with export = When Needed

TYPESCRIPT
declare function legacy(input: string): number;
export = legacy;

Why it matters: many older packages still use CommonJS. Using export = keeps interop correct without forcing awkward import syntax for users.

Item 113: Add JSDoc for Editor Support

TYPESCRIPT
/**
 * Parses a date string. Throws if invalid.
 */
export function parseDate(input: string): Date;

Why it matters: good JSDoc improves autocomplete and gives consumers the intent behind a type, not just the signature.

Item 114: Keep Declarations in Sync With Runtime

Declarations must reflect reality. If runtime behavior changes, update the .d.ts.

Warning

Incorrect declarations are worse than no declarations because they mislead users and tools.

Why it matters: if your .d.ts lies, developers ship bugs confidently. Keeping them synchronized avoids a false sense of safety.

Scenario: Typing a Legacy SDK

TYPESCRIPT
// types/legacy-sdk.d.ts
declare module "legacy-sdk" {
  export interface Payment {
    id: string;
    amount: number;
    status: "pending" | "paid" | "failed";
  }
 
  export function createPayment(amount: number): Promise<Payment>;
}

Key Takeaways

  1. Use declare module for external packages.
  2. Use declare global sparingly.
  3. Model functions, objects, and classes explicitly.
  4. Use overloads for API variants.
  5. Keep declarations in sync with runtime behavior.

Next: strict mode and compiler options.