How duck-ttest compares
Honest tradeoffs versus tsd, expect-type, and type-fest.
The TypeScript ecosystem already has three well-known options that overlap with duck-ttest. Pick the right tool for your situation.
The space
| duck-ttest | tsd | expect-type | type-fest | |
|---|---|---|---|---|
| What it is | Assertion API + utility library | Assertion API, CLI runner | Assertion API, fluent | Utility library |
| Runner | Your tsc --noEmit | Bundled CLI on top of tsc | Your tsc --noEmit | n/a — types only |
| Assertion style | AssertTrue<Equal<X, Y>, Msg> | expectType<X>(value) | expectTypeOf(value).toEqualTypeOf<X>() | n/a |
| Strict equality | Equal<X, Y> (variance-preserving) | expectType is structural | .toEqualTypeOf is variance-preserving | n/a |
| Utility breadth | 35 modules — guards, brands, SQL, router, fp, geometry, ... | None | None | Broad object/string/number utilities |
| Runtime cost | Zero (type-only) | Zero | Zero (compile time) — small runtime function for fluent API | Zero |
| Custom error messages | Msg generic on every assertion | TS diagnostic | TS diagnostic | n/a |
| Sub-path imports | Per-module | Single import | Single import | Per-utility |
| Best for | Schema generators, ORMs, type-heavy SDKs | Public APIs that ship type tests | Drop-in expect-style ergonomics | Stocking your own type lib |
When to pick duck-ttest
- You want assertions and a utility library in one package.
- You already structure your tests around
tsc --noEmit. - You care about distinguishing
anyfromunknown(useEqual, not extends-extends). - You ship a framework where the type system itself is the contract (schema builders, ORMs, routers).
When to pick tsd instead
- Your library publishes a
.d.tsand you want a CLI that runs over atest-d/folder as part ofnpm test. - You don't care about a custom utility library — only assertion ergonomics.
When to pick expect-type instead
- You want a
.toEqualTypeOf<T>()fluent style for readability. - You don't need a utility library next door.
When to pick type-fest instead
- You only need utility types (
Simplify,RequireAtLeastOne,Get, ...). - You don't write assertions.
duck-ttest can be used alongside any of the above. They aren't mutually exclusive — type-fest's utilities will type-check just fine in a duck-ttest assertion.
What duck-ttest is not
- Not a runtime test runner. Use vitest / jest / bun-test for runtime behavior; pair them with duck-ttest for the type side.
- Not a CLI. There is no
ttestbinary.tsc --noEmitis the runner. - Not a Zod / Valibot alternative. Those validate values at runtime. duck-ttest only checks types at compile time.
See also
- Getting started — the five-minute tour.
- Core / Type-level testing — combining duck-ttest with
@ts-expect-errorandtsd.