TypeScript Declaration Authoring
A declaration file can describe a plain-JavaScript library at depth with overloaded function declarations. The intermediate course wrote basic declaration files, and this lesson goes beyond them with overloads and an options interface.
The toolkit.js file
The library is plain JavaScript with no types, and it assigns an object of two functions to module.exports. The first line switches off type checking inside this file.
// @ts-nocheck
"use strict";
function formatPrice(amount, options) {
let symbol = "$";
let places = 2;
if (typeof options === "string") {
symbol = options;
} else if (options !== undefined) {
if (options.symbol !== undefined) {
symbol = options.symbol;
}
if (options.places !== undefined) {
places = options.places;
}
}
return symbol + amount.toFixed(places);
}
function parsePrice(text) {
return Number(text.replace(/[^0-9.]/g, ""));
}
module.exports = { formatPrice, parsePrice };The toolkit.d.ts file
The declaration file gives formatPrice three overloads and names the options shape with an interface. It holds declarations only, with no function bodies.
export interface PriceOptions {
symbol?: string;
places?: number;
}
export declare function formatPrice(amount: number): string;
export declare function formatPrice(amount: number, options: PriceOptions): string;
export declare function formatPrice(amount: number, symbol: string): string;
export declare function parsePrice(text: string): number;Calling through the declarations
Each call matches a different overload, and the options object is checked against the PriceOptions interface. The output comes from the JavaScript file.
import { formatPrice, parsePrice } from './toolkit';
import type { PriceOptions } from './toolkit';
const options: PriceOptions = { symbol: 'EUR ', places: 0 };
console.log(formatPrice(12.5));
console.log(formatPrice(12.5, options));
console.log(formatPrice(12.5, 'ยฃ'));
console.log(parsePrice('$12.50'));A call the overloads refuse
A boolean matches no overload, since the second argument must be an options object or a symbol string. The checker refuses the call before the program runs.
import { formatPrice } from './toolkit';
console.log(formatPrice(12.5, true));The export = form
The stamp module exports one function value, so the module ends with export = instead of named exports. The compiled JavaScript assigns that one value to module.exports.
function stamp(text: string): string {
return '[' + text + ']';
}
namespace stamp {
export const version = '1.0';
}
console.log(stamp('ready'));
console.log(stamp.version);
export = stamp;Example
import { formatPrice, parsePrice } from './toolkit';
import type { PriceOptions } from './toolkit';
const options: PriceOptions = { symbol: 'USD ', places: 2 };
const amounts: number[] = [4, 12.5, 100];
for (const amount of amounts) {
console.log(formatPrice(amount, options));
}
console.log(parsePrice('USD 42.75'));Key points
- An overloaded declaration lists several call signatures for one function, and the checker tries the signatures in order.
- An options interface names the shape of an options object, so the overloads reference the shape by name.
- A call that matches no declared overload is refused at compile time.
- The export = assignment states that the whole module export is one value.
- A declaration file for a CommonJS library that assigns one value to module.exports ends with the same export = line.
In this example, the consumer formats each amount through the options overload and parses a price through the second declared function. Every call is checked against the declaration file, and every line of output comes from the JavaScript library.
Note
The JavaScript file and the declaration file share the base name toolkit, so one import finds both halves.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท TypeScript Playground
Exercise
What happens when a call matches none of the declared overloads?