Declaration Files Deep Dive
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
// 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
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
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
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
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
/**
* 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
// 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
- Use
declare modulefor external packages. - Use
declare globalsparingly. - Model functions, objects, and classes explicitly.
- Use overloads for API variants.
- Keep declarations in sync with runtime behavior.
Next: strict mode and compiler options.