Difficulty: Advanced
Declaration files (.d.ts) are TypeScript files that contain only type information and no runtime code. They serve as a bridge between TypeScript's type system and JavaScript libraries that were not written in TypeScript. When TypeScript encounters a .d.ts file, it uses the type information for type checking but does not emit any JavaScript from it. Declaration files are the backbone of TypeScript's interoperability with the vast JavaScript ecosystem.
The `declare` keyword is used to tell TypeScript about the shape of code that exists elsewhere - in a JavaScript library, a global variable injected by a script tag, or a runtime environment API. When you write `declare function jQuery(selector: string): any`, you are not creating a function; you are telling TypeScript that this function exists at runtime and describing its type signature. The `declare` keyword can be applied to variables, functions, classes, enums, namespaces, and modules.
Ambient module declarations allow you to type non-TypeScript modules such as CSS files, JSON files, images, or any other asset that your bundler can import. By writing `declare module '*.css'`, you tell TypeScript that any CSS import will resolve to a specific type. This is commonly used in projects with Webpack or Vite loaders that allow importing non-JavaScript assets.
To create declaration files for your own code, you can either write them manually or let the TypeScript compiler generate them using the `declaration: true` option in tsconfig.json. When publishing a library, you should include .d.ts files alongside your compiled JavaScript so that TypeScript consumers get full type checking. The `types` or `typings` field in package.json points to your entry declaration file.
// globals.d.ts - declaring global variables and functions
// declare const API_URL: string;
// declare function fetchData(endpoint: string): Promise<any>;
// declare class EventBus {
// on(event: string, callback: Function): void;
// emit(event: string, data?: any): void;
// }
// Simulating declared globals
const API_URL: string = 'https://api.example.com';
function fetchData(endpoint: string): Promise<any> {
return Promise.resolve({ url: `${API_URL}${endpoint}` });
}
class EventBus {
on(event: string, callback: Function): void {
console.log(`Listener added for: ${event}`);
}
emit(event: string, data?: any): void {
console.log(`Event emitted: ${event}`);
}
}
console.log(API_URL);
const bus = new EventBus();
bus.on('click', () => {});
bus.emit('click');
The declare keyword tells TypeScript about values that exist at runtime but are defined elsewhere. In a .d.ts file, all declarations are implicitly ambient.
// In a .d.ts file, you would write:
// declare module '*.css' {
// const classes: Record<string, string>;
// export default classes;
// }
// declare module '*.svg' {
// const src: string;
// export default src;
// }
// declare module '*.png' {
// const src: string;
// export default src;
// }
// Simulating what these declarations enable:
interface CSSModule {
[className: string]: string;
}
const styles: CSSModule = {
container: 'container_x7k2',
header: 'header_a3b1',
button: 'button_m9p4'
};
console.log(styles.container);
console.log(styles.header);
console.log(Object.keys(styles).length + ' CSS classes');
Ambient module declarations tell TypeScript what type to assign to imports that match a given pattern. This is essential for bundlers that support importing non-JS assets.
// When you compile with { declaration: true }, TypeScript generates .d.ts files
// Source: math-lib.ts
// export function add(a: number, b: number): number { return a + b; }
// export interface MathResult { value: number; operation: string; }
// Generated: math-lib.d.ts
// export declare function add(a: number, b: number): number;
// export interface MathResult { value: number; operation: string; }
// The .d.ts file strips implementation, keeping only type signatures
function add(a: number, b: number): number { return a + b; }
interface MathResult {
value: number;
operation: string;
}
const result: MathResult = { value: add(3, 4), operation: 'addition' };
console.log(result.value);
console.log(result.operation);
The TypeScript compiler can auto-generate .d.ts files that strip out implementations, leaving only type signatures for consumers of your library.
declaration files, .d.ts, declare keyword, ambient modules, ambient declarations, global augmentation