or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced

combinators.mdconcurrency-testing.mdmodel-based-testing.md
configuration.mdindex.md
tile.json

combinators.mddocs/advanced/

Combinators and Transformers

Combinators compose and transform arbitraries to create custom generators. They enable building complex arbitraries from simpler ones while preserving shrinking behavior.

Capabilities

Constant Values

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' }
);

Optional Values

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';
  })
);

One Of Multiple Arbitraries

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';
    }
  )
);

Cloning Values

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;
  })
);

Arbitrary Modifiers

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);

Recursive Structures

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;
  })
);

Entity Graphs

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;
  })
);

Function Arbitraries

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 Definitions

type DepthSize = 'small' | 'medium' | 'large';
type DepthIdentifier = { readonly name: string };