Technology · TypeScript
TypeScript Declaration Files
Write .d.ts files, publish typed npm packages, and create type stubs.
TL;DR
- 01Create .d.ts files to provide types for JavaScript libraries.
- 02Use declare keyword to define types without implementation.
- 03Publish types alongside your npm package for TypeScript users.
Writing Declaration Files
- Create .d.ts files to define types for JavaScript code.
// mylib.d.ts export function greet(name: string): string; export interface User { id: number; name: string; } - Declaration files contain only type definitions, no implementation.
// index.d.ts export class Calculator { add(a: number, b: number): number; subtract(a: number, b: number): number; } - Use declare to define types for existing JavaScript globals.
declare global { interface Window { myGlobal: string; } } - Declare ambient variables that exist at runtime but not in source.
declare const __VERSION__: string; declare function require(module: string): any; - Use declare module for UMD or non-ES library entry points.
export as namespace MyLib; export function init(options: Options): void; export interface Options { debug?: boolean; }
Module Declaration
- Declare types for entire modules or namespaces.
declare module "my-library" { export function process(data: any): any; export const version: string; } - Augment existing modules with additional types.
declare module "express" { interface Request { userId?: number; } } - Use triple-slash directives to reference other declaration files.
/// <reference path="./types.d.ts" /> /// <reference types="node" /> - Organize types with namespaces for large libraries.
declare namespace MyLib { interface Config { } function init(config: Config): void; } - Use a wildcard module declaration for file imports like CSS or SVG.
declare module "*.svg" { const content: string; export default content; } declare module "*.css" { const styles: Record<string, string>; export default styles; }
Package Configuration
- Point to declaration files in package.json with the types field.
{ "name": "my-library", "version": "1.0.0", "main": "dist/index.js", "types": "dist/index.d.ts" } - Emit declaration files from TypeScript during build.
{ "compilerOptions": { "declaration": true, "declarationDir": "./dist", "outDir": "./dist" } } - Use typesVersions to support different TypeScript versions.
{ "typesVersions": { "<=4.0": { "*": ["ts4.0/*"] }, ">=4.1": { "*": ["*"] } } } - Use exports map with types conditions for dual CJS/ESM packages.
{ "exports": { ".": { "import": { "types": "./dist/esm/index.d.ts", "default": "./dist/esm/index.js" }, "require": { "types": "./dist/cjs/index.d.ts", "default": "./dist/cjs/index.js" } } } } - Generate declaration maps for go-to-definition in source.
{ "compilerOptions": { "declarationMap": true, "declaration": true, "sourceRoot": "./src" } }
Publishing to DefinitelyTyped
- Contribute type definitions for popular untyped libraries.
DefinitelyTyped/types/ library-name/ index.d.ts package.json tsconfig.json tests.ts - Test declaration files with sample code before submitting.
// tests.ts import * as lib from "./index"; const result: string = lib.process("test"); const version: string = lib.version; - Include a package.json in the types folder with the correct shape.
{ "name": "@types/library-name", "version": "1.0.0", "typings": "index.d.ts" } - Add a tsconfig.json to validate declarations during CI.
{ "compilerOptions": { "module": "commonjs", "lib": ["es6"], "noImplicitAny": true, "noImplicitThis": true, "strictNullChecks": true, "strictFunctionTypes": true } } - Run dtslint to catch type errors before opening a pull request.
npm install -g dtslint dtslint types/library-name
Best Practices
- Keep declaration files in sync with implementation.
// Don't declare types that don't match the implementation export function add(a: number, b: number): number; // Should match: const add = (a, b) => a + b; - Export only public APIs and hide internal helpers.
// Correct: public types are exported export interface PublicAPI { } // Wrong: internal types shouldn't be exported // export interface _InternalHelper { } - Include JSDoc comments for better IDE support.
/** * Processes data asynchronously * @param data - The input data to process * @returns A promise that resolves with the result */ export function processAsync(data: any): Promise<any>; - Use semver versioning for types to avoid breaking changes.
# Bump patch for additions, minor for new overloads, major for removals npm version patch # safe: new optional param npm version major # breaking: removed export - Avoid using any in declaration files — prefer unknown or generics.
// Weak: loses type information export function parse(input: string): any; // Better: let the caller specify the return type export function parse<T>(input: string): T;
Tip: Always include declaration files with your npm package or publish to DefinitelyTyped — TypeScript users will appreciate the type safety.
Warning: Keep declaration files accurate and in sync with implementation — incorrect types are worse than no types at all.