Home

Awesome

TypeScript Definition Style Guide

Style guide for adding type definitions to my npm packages

Open an issue if anything is unclear or if you have ideas for other checklist items.

This style guide assumes your package is native ESM.

Checklist

Check out this, this, and this example for how it should be done.

Types

Prefer read-only values

Make something read-only when it's not meant to be modified. This is usually the case for return values and option interfaces. Get familiar with the readonly keyword for properties and array/tuple types. There's also a Readonly type to mark all properties as readonly.

Before:

type Point = {
	x: number;
	y: number;
	children: Point[];
};

After:

type Point = {
	readonly x: number;
	readonly y: number;
	readonly children: readonly Point[];
};

Import types explicitly

Don't use implicit global types except for built-ins or when they can't be imported.

Before:

export function getWindow(): Electron.BrowserWindow;

After:

import {BrowserWindow} from 'electron';

export function getWindow(): BrowserWindow;

Readable named imports

Use a readable name when using named imports.

Before:

import {Writable} from 'node:stream';

After:

import {Writable as WritableStream} from 'node:stream';

Documentation

Exported definitions should be documented with TSDoc. You can borrow text from the readme.

Example:

export type Options = {
	/**
	Allow negative numbers.

	@default true
	*/
	readonly allowNegative?: boolean;

	/**
	Has the ultimate foo.

	Note: Only use this for good.

	@default false
	*/
	readonly hasFoo?: boolean;

	/**
	Where to save.

	Default: [User's downloads directory](https://example.com)

	@example
	```
	import add from 'add';

	add(1, 2, {saveDirectory: '/my/awesome/dir'})
	```
	*/
	readonly saveDirectory?: string;
};

/**
Add two numbers together.

@param x - The first number to add.
@param y - The second number to add.
@returns The sum of `x` and `y`.
*/
export default function add(x: number, y: number, options?: Options): number;

Note:

Code examples

Testing

The type definition should be tested with tsd. Example of how to integrate it.

Example:

import {expectType} from 'tsd';
import delay from './index.js';

expectType<Promise<void>>(delay(200));

expectType<Promise<string>>(delay(200, {value: '🦄'}));
expectType<Promise<number>>(delay(200, {value: 0}));

expectType<Promise<never>>(delay.reject(200, {value: '🦄'}));
expectType<Promise<never>>(delay.reject(200, {value: 0}));

When it makes sense, also add a negative test using expectError().

Note: