What it is
tsd tests your TypeScript type definitions, asserting that types resolve as intended and that invalid usage is correctly rejected.
Write assertions in a .test-d.ts file using expectType, expectError and friends. tsd type-checks them and fails when an assertion does not hold.
Installation
npm install --save-dev tsdGetting started
The smallest useful thing you can do with it, and what each part means.
typescript
import { expectType, expectError, expectAssignable } from 'tsd';
import { pick } from './index';
const user = { id: 1, name: 'Ada', password: 'secret' };
// The return type must contain exactly the requested keys.
expectType<{ id: number; name: string }>(pick(user, ['id', 'name']));
// Requesting a key that does not exist must be rejected.
expectError(pick(user, ['nope']));
expectAssignable<object>(pick(user, ['id']));Advanced usage
Where the library earns its place over a simpler alternative.
typescript
import { expectNotAny, expectNotType } from 'tsd';
expectNotAny(parseConfig('{}'));
// Guard against a refactor widening a literal union to string.
expectNotType<string>(getRole());
expectType<'admin' | 'member'>(getRole());
// In package.json:
// { "scripts": { "test": "tsd && vitest run" } }Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Cannot find module for the package
- tsd resolves the types field in package.json. Build the declarations first, or point it at the source with a tsd config.
- expectType fails on seemingly identical types
- It is an exact identity check. A readonly modifier or an optional marker counts as different — use expectAssignable when that is acceptable.
Best practices
- Run tsd in CI alongside runtime tests — type regressions are invisible otherwise.
- Assert with expectNotAny on every public entry point.
- Use expectError to prove that misuse is actually rejected.
- Keep type tests next to the public API they cover, not scattered.
Background
Why it exists, and what it was reacting to.
Written by Sindre Sorhus for library authors: a package's types are part of its public API, and without tests a refactor can silently widen a type to any while every runtime test still passes.
