typescript / expert
Snippet
Property-Based Testing with Type-Safe Generators
Example-based tests check single inputs; property-based tests assert invariants over a generated sample space. Modelling a generator as `Gen<T> = () => T` makes them composable through plain functions — `array(int(...))` is a generator of arrays of ints — and TypeScript carries `T` through the combinator chain so the property receives the correct type. The `check` driver runs hundreds of pseudo-random samples and throws on the first counter-example, surfacing edge cases hand-written tests rarely cover.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Gen<T> = () => T;const int = (min: number, max: number): Gen<number> =>() => Math.floor(Math.random() * (max - min + 1)) + min;const array = <T>(g: Gen<T>, max = 20): Gen<T[]> =>() => Array.from({ length: int(0, max)() }, g);function check<T>(gen: Gen<T>, prop: (x: T) => boolean, runs = 100): void {for (let i = 0; i < runs; i++) {const sample = gen();if (!prop(sample)) throw new Error(`failed for ${JSON.stringify(sample)}`);}}check(array(int(-50, 50)), (xs) => xs.slice().reverse().reverse().length === xs.length);
Breakdown
1
type Gen<T> = () => T;
Generators are nullary thunks returning T — easy to compose.
2
const array = <T>(g: Gen<T>, max = 20): Gen<T[]> =>
Combinator: lifts a value generator into a generator of arrays.
3
function check<T>(gen: Gen<T>, prop: (x: T) => boolean, runs = 100): void {
Runs the property `runs` times against fresh samples.
4
check(array(int(-50, 50)), (xs) => xs.slice().reverse().reverse().length === xs.length);
Double-reverse preserves length — an invariant verified over many random arrays.