Type-driven tests: every expectTypeOf pairs an assert, covering the header matrix (exhaustive vs partial patterns, strict R vs widened returns, string and numeric keys, _ fallback wiring). Negative cases use Parameters<typeof factory>[0] assignability because expect-type's .not.toBeCallableWith collapses to never on these generic Simplify<>-wrapped signatures. Same documented expectTypeOf floating-promise false positive as index.test.ts, so the same file-level disable applies; testing.md known-issue updated to list both files.
2.0 KiB
2.0 KiB
Testing
For this library the types are the feature, so a runtime-only test loop would verify the wrong thing. The commands are in CONTRIBUTING.md; this file records why the loop is shaped the way it is.
Type-driven development
The rules — the loop and the pairing rule — are in CONTRIBUTING.md § Testing discipline (type-driven). What follows is why and what was rejected.
Decision (2026-09)
The compile-time expectation is written before the runtime assertion, and both before the implementation.
Why
- A runtime-only test can pass while the type is wrong, so a type-level library would ship a broken feature its tests bless.
- The type error is a more precise spec than a failing assertion, because it states the exact expected type before the logic exists.
Rejected
- Runtime-first (classic red/green): it verifies the value, not the contract, and the contract is the product.
- Testing the type only: it would not catch handler wiring,
exhaustive()throwing, or theotherwisefallback (seesrc/index.test.ts).
The runner is node --test --strip-types "src/**/*.test.ts" and the tiers are in
CONTRIBUTING.md § Development commands.
c8 uses V8 coverage, so the --strip-types source is instrumented without a
build step, and the runner relies on the .ts import-extension convention (see
tooling.md).
Known issues
- The type-aware linter misidentifies
expectTypeOf()as a floating promise, so test files that use it (src/index.test.ts,src/primitive.test.ts) carry a file-leveloxlint-disable typescript/no-floating-promiseswith an explanatory comment. It is a known false positive, not a rule worth disabling project-wide (see tooling.md § oxlint-disable directives live next to the code).