Declaration Files
HardDeclaration files (`.d.ts`) describe the types of JavaScript code without containing any implementation. They are how TypeScript understands third-party JavaScript libraries, global variables, and module shapes. The DefinitelyTyped repository (`@types/*` packages) provides declaration files for thousands of libraries. Understanding how to read, write, and troubleshoot declaration files is essential for working with TypeScript in real projects.
Interactive Visualization
Key Points
- Declaration files (`.d.ts`) contain only type information — no runtime code
- `declare` keyword introduces ambient declarations for existing JavaScript
- `@types/*` packages from DefinitelyTyped provide types for JS libraries
- Module augmentation lets you extend existing module types
- Global augmentation adds types to the global scope
- Triple-slash directives (`/// <reference>`) link declaration files together
- `declare module "name"` creates type definitions for untyped modules
Code Examples
Writing Declaration Files
// types/analytics.d.ts — declare types for a JS analytics library declare module 'analytics-lib' { interface AnalyticsConfig { apiKey: string debug?: boolean batchSize?: number } interface Event { name: string properties?: Record<string, unknown> timestamp?: Date } export function init(config: AnalyticsConfig): void export function track(event: Event): Promise<void> export function identify(userId: string, traits?: Record<string, unknown>): void } // Usage in application code import { init, track } from 'analytics-lib' init({ apiKey: 'abc123', debug: true }) track({ name: 'page_view', properties: { path: '/home' } })
Declaration files describe the API surface of JavaScript libraries. The `declare module` syntax creates type definitions that TypeScript uses for type checking imports.
Module Augmentation
// Extend Express Request with custom properties import 'express' declare module 'express' { interface Request { userId?: string sessionId?: string } } // Now TypeScript recognizes these properties import { Request, Response } from 'express' function authMiddleware(req: Request, _res: Response, next: () => void): void { req.userId = 'user-123' // No type error next() } // Extend a library's types with new methods declare module 'dayjs' { interface Dayjs { businessDaysAdd(days: number): Dayjs } }
Module augmentation lets you add new properties or methods to existing types from third-party libraries without modifying the original source code.
Global & Ambient Declarations
// global.d.ts — add global types declare global { interface Window { __APP_CONFIG__: { apiUrl: string version: string features: Record<string, boolean> } } // Declare a global function (e.g., injected by a script tag) function gtag(command: string, ...args: unknown[]): void } // Environment variable types for Vite or Next.js declare namespace NodeJS { interface ProcessEnv { DATABASE_URL: string API_SECRET: string NODE_ENV: 'development' | 'production' | 'test' } } export {} // This makes the file a module (required for declare global)
Global declarations extend the global scope with custom types. The `declare global` block adds types to Window, process.env, or any global interface.
Common Mistakes
- Forgetting the `export {}` in files that use `declare global` (makes it a module)
- Confusing `declare module` (module augmentation) with regular module declarations
- Not checking DefinitelyTyped before writing custom declarations for popular libraries
- Placing declaration files outside the TypeScript compilation scope (not included in tsconfig)
- Writing implementation code in `.d.ts` files — they should only contain type declarations
Interview Tips
- Know how to write a basic declaration file for an untyped JavaScript library
- Explain module augmentation with a practical example like extending Express Request
- Mention DefinitelyTyped and `@types/*` packages as the standard type source
- Understand the difference between `declare module` for new modules vs augmentation