Combinators compose and transform arbitraries to create custom generators. They enable building complex arbitraries from simpler ones while preserving shrinking behavior.
Generate constant or randomly selected constant values.
/**
* Always generate the same constant value
* @param value - The constant value to generate
* @returns Arbitrary always generating the same value
*/
function constant<T>(value: T): Arbitrary<T>;
/**
* Generate one value randomly selected from provided constants
* @param values - List of constant values to choose from
* @returns Arbitrary generating one of the constants
*/
function constantFrom<T>(...values: T[]): Arbitrary<T>;
/**
* Generate values by mapping integers to constants using weighted groups
* @param entries - Weighted groups of value builders
* @returns Arbitrary generating values from weighted groups
*/
function mapToConstant<T>(...entries: { num: number; build: (idInGroup: number) => T }[]): Arbitrary<T>;Usage Examples:
import { constant, constantFrom, mapToConstant, property, assert } from 'fast-check';
// Always generate the same value
assert(
property(constant(42), (n) => {
return n === 42;
})
);
// Choose from a set of values
assert(
property(constantFrom('red', 'green', 'blue'), (color) => {
return ['red', 'green', 'blue'].includes(color);
})
);
// Weighted selection
const weightedColors = mapToConstant(
{ num: 5, build: () => 'red' },
{ num: 3, build: () => 'green' },
{ num: 2, build: () => 'blue' }
);Generate optional values (value or nil).
/**
* Generate either nil (default: null) or a value from source arbitrary
* @param arb - Source arbitrary
* @param constraints - Nil value and frequency constraints
* @returns Arbitrary generating value or nil
*/
function option<T, TNil = null>(
arb: Arbitrary<T>,
constraints?: OptionConstraints<TNil>
): Arbitrary<T | TNil>;
interface OptionConstraints<TNil> {
nil?: TNil;
freq?: number;
depthIdentifier?: DepthIdentifier | string;
}Usage Example:
import { option, string, property, assert } from 'fast-check';
// Optional string (string or null)
assert(
property(option(string()), (maybeStr) => {
return maybeStr === null || typeof maybeStr === 'string';
})
);
// Optional with custom nil
assert(
property(option(string(), { nil: undefined }), (maybeStr) => {
return maybeStr === undefined || typeof maybeStr === 'string';
})
);Generate values from one of several arbitraries.
/**
* Generate values from one of several arbitraries
* @param arbs - List of arbitraries or constraints with arbitraries
* @returns Arbitrary generating values from one of the sources
*/
function oneof<Ts extends MaybeWeightedArbitrary<unknown>[]>(
...arbs: Ts | [OneOfConstraints, ...Ts]
): Arbitrary<OneOfValue<Ts>>;
interface OneOfConstraints {
maxDepth?: number;
depthSize?: DepthSize;
depthIdentifier?: DepthIdentifier | string;
withCrossShrink?: boolean;
}
type MaybeWeightedArbitrary<T> = Arbitrary<T> | WeightedArbitrary<T>;
interface WeightedArbitrary<T> {
arbitrary: Arbitrary<T>;
weight: number;
}
type OneOfValue<Ts extends readonly unknown[]> =
Ts[number] extends Arbitrary<infer T> ? T :
Ts[number] extends WeightedArbitrary<infer T> ? T :
never;Usage Examples:
import { oneof, string, integer, boolean, property, assert } from 'fast-check';
// Choose from multiple arbitraries
assert(
property(oneof(string(), integer(), boolean()), (value) => {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
);
})
);
// Weighted selection
assert(
property(
oneof(
{ arbitrary: string(), weight: 5 },
{ arbitrary: integer(), weight: 1 }
),
(value) => {
return typeof value === 'string' || typeof value === 'number';
}
)
);Generate N identical copies of a value.
/**
* Clone values generated by an arbitrary to produce N identical copies
* @param arb - Source arbitrary
* @param numValues - Number of clones to produce
* @returns Arbitrary generating tuple of N identical values
*/
function clone<T, N extends number>(arb: Arbitrary<T>, numValues: N): Arbitrary<CloneValue<T, N>>;
type CloneValue<T, N extends number> = { [K in number]: T } & { length: N };Usage Example:
import { clone, string, property, assert } from 'fast-check';
// Generate 3 identical strings
assert(
property(clone(string(), 3), ([a, b, c]) => {
return a === b && b === c;
})
);Modify arbitrary behavior for shrinking and bias.
/**
* Disable shrinking capabilities of an arbitrary
* @param arb - Source arbitrary
* @returns Arbitrary without shrinking
*/
function noShrink<T>(arb: Arbitrary<T>): Arbitrary<T>;
/**
* Disable bias in value generation (forces uniform distribution)
* @param arb - Source arbitrary
* @returns Arbitrary without bias
*/
function noBias<T>(arb: Arbitrary<T>): Arbitrary<T>;
/**
* Limit the number of shrink attempts for an arbitrary
* @param arbitrary - Source arbitrary
* @param maxShrinks - Maximum number of shrink operations
* @returns Arbitrary with limited shrinking
*/
function limitShrink<T>(arbitrary: Arbitrary<T>, maxShrinks: number): Arbitrary<T>;Usage Examples:
import { noShrink, noBias, limitShrink, integer, property, assert } from 'fast-check';
// Disable shrinking (useful for debugging or performance)
const noShrinkInt = noShrink(integer());
// Force uniform distribution
const uniformInt = noBias(integer({ min: 0, max: 100 }));
// Limit shrink attempts (useful for complex generators)
const limitedInt = limitShrink(integer(), 100);Create recursive and mutually recursive arbitraries.
/**
* Create mutually recursive arbitraries using tie function
* @param builder - Function that builds recursive structure
* @returns Object containing all defined arbitraries
*/
function letrec<T>(
builder: LetrecTypedBuilder<T> | LetrecLooselyTypedBuilder<T>
): LetrecValue<T>;
/**
* Create memoized recursive arbitraries with depth control
* @param builder - Function receiving maxDepth and returning arbitrary
* @returns Memoized arbitrary function
*/
function memo<T>(builder: (maxDepth: number) => Arbitrary<T>): Memo<T>;
type LetrecValue<T> = { [K in keyof T]: Arbitrary<T[K]> };
type LetrecTypedBuilder<T> = (tie: LetrecTypedTie<T>) => LetrecValue<T>;
type LetrecTypedTie<T> = <K extends keyof T>(key: K) => Arbitrary<T[K]>;
type Memo<T> = (maxDepth?: number) => Arbitrary<T>;Usage Examples:
import { letrec, memo, record, array, string, integer, property, assert, option } from 'fast-check';
// Binary tree using letrec
type Tree = { value: number; left: Tree | null; right: Tree | null };
const treeArb = letrec((tie) => ({
tree: record({
value: integer(),
left: option(tie('tree')),
right: option(tie('tree')),
}),
})).tree;
// Nested array using memo
const nestedArrayArb = memo((maxDepth) => {
if (maxDepth <= 0) {
return integer();
}
return oneof(integer(), array(nestedArrayArb(maxDepth - 1)));
});
assert(
property(nestedArrayArb(3), (value) => {
return value !== undefined;
})
);Generate interconnected entities with relationships.
/**
* Generate interconnected entities with relationships
* @param arbitraries - Arbitraries for entity fields
* @param relations - Relationship definitions between entities
* @param constraints - Size and depth constraints
* @returns Arbitrary generating entity graphs
*/
function entityGraph<TEntityFields, TEntityRelations>(
arbitraries: EntityGraphArbitraries<TEntityFields>,
relations: TEntityRelations,
constraints?: EntityGraphContraints<TEntityFields>
): Arbitrary<EntityGraphValue<TEntityFields, TEntityRelations>>;
type EntityGraphArbitraries<TEntityFields> = {
[K in keyof TEntityFields]: Arbitrary<TEntityFields[K]>;
};
type EntityGraphRelations<TEntityFields> = {
[relationName: string]: [keyof TEntityFields, keyof TEntityFields, number, number];
};
type EntityGraphContraints<TEntityFields> = {
maxDepth?: number;
depthIdentifier?: DepthIdentifier | string;
[K in keyof TEntityFields]?: { num: number };
};
type EntityGraphValue<TEntityFields, TEntityRelations> = {
[K in keyof TEntityFields]: Array<TEntityFields[K] & { [rel: string]: any }>;
};Usage Example:
import { entityGraph, string, integer, record, property, assert } from 'fast-check';
// User and Post entities with relationships
const socialGraph = entityGraph(
{
user: record({ name: string(), age: integer({ min: 13, max: 120 }) }),
post: record({ title: string(), content: string() }),
},
{
author: ['post', 'user', 1, 1], // Each post has exactly 1 author
posts: ['user', 'post', 0, 10], // Each user has 0-10 posts
}
);
assert(
property(socialGraph, (graph) => {
return graph.user.length > 0 || graph.post.length > 0;
})
);Generate pure functions with deterministic behavior.
/**
* Generate pure functions that deterministically map inputs to outputs
* @param arb - Arbitrary for function output values
* @returns Arbitrary generating functions
*/
function func<TArgs extends any[], TOut>(arb: Arbitrary<TOut>): Arbitrary<(...args: TArgs) => TOut>;
/**
* Generate comparison functions suitable for Array.sort
* @returns Arbitrary generating comparison functions
*/
function compareFunc<T>(): Arbitrary<(a: T, b: T) => number>;
/**
* Generate boolean comparison functions
* @returns Arbitrary generating boolean comparison functions
*/
function compareBooleanFunc<T>(): Arbitrary<(a: T, b: T) => boolean>;Usage Examples:
import { func, compareFunc, integer, string, array, property, assert } from 'fast-check';
// Generate functions
assert(
property(func<[number, number], string>(string()), (fn) => {
const result1 = fn(1, 2);
const result2 = fn(1, 2);
return result1 === result2; // Deterministic
})
);
// Generate comparison functions
assert(
property(array(integer()), compareFunc<number>(), (arr, cmp) => {
const sorted = [...arr].sort(cmp);
return sorted.length === arr.length;
})
);type DepthSize = 'small' | 'medium' | 'large';
type DepthIdentifier = { readonly name: string };