Unions
Union types are forged by rolling one member at runtime. Nullable members are dropped first, so the result is always a meaningful value.
Rolling a member
type Id = string | number;
const id = Forger.create<Id>()!;
// sometimes a string, sometimes a number — rolled per call
Members may be of any kind — primitives, objects, dates, enums, literals:
type Result = { ok: true; value: string } | { ok: false; error: Date };
const result = Forger.create<Result>()!;
// one of the two object shapes, fully populated
null and undefined members are dropped
Nullable unions always produce the non-null side:
const a = Forger.create<null | number>()!; // typeof a === 'number'
const b = Forger.create<number | null>()!; // typeof b === 'number'
const c = Forger.create<undefined | number>()!; // typeof c === 'number'
This matches how fakes are consumed: a test double should carry data, not absence. It also makes create<T>()! safe to use on API-shaped types that were made "optional" with | null.
Mixing literals and types
Literal and non-literal members can be combined freely; every member is a candidate:
const value = Forger.create<'auto' | number>()!;
// 'auto' — or a random number
Literal-only unions
When all members are literals, the union behaves like a value set — see Literals for details and the comparison with enums.
Arrays of unions
Each element is rolled independently:
const statuses = Forger.create<('new' | 'done')[]>()!;
// ['done', 'new', 'done'] — for example
08 September 2026