import { type Type } from '../type.mjs'; /** * Creates a recursive type definition for self-referential or mutually recursive data structures. * * This function enables defining types that refer to themselves (like linked lists, trees) * or mutually refer to each other (like even/odd number chains). * * @template A - The type to be defined recursively * @param typeName - A descriptive name for the recursive type * @param definition - A function that returns the Type definition. This function is called * lazily to allow self-reference * @param options - Optional configuration * @param options.defaultValue - Explicit default value for the type. Recommended for * mutually recursive types to avoid infinite loops * * @returns A Type that can validate and work with recursive structures * * @example * // Simple recursive type: Linked list * ```ts * import * as t from 'ts-fortress'; * * type LinkedList = Readonly<{ * value: number; * next: LinkedList | null; * }>; * * const LinkedListNumber: t.Type = t.recursion('LinkedList', () => * t.record({ * value: t.number(), * next: t.union([t.nullType, LinkedListNumber]), * }), * ); * ``` * * @example * // Tree structure * ```ts * import * as t from 'ts-fortress'; * * type TreeNode = Readonly<{ * value: T; * children: readonly TreeNode[]; * }>; * * const TreeNodeString: t.Type> = t.recursion( * 'TreeNode', * () => * t.record({ * value: t.string(), * children: t.array(TreeNodeString), * }), * ); * ``` * * @example * // Mutually recursive types: Even/Odd chain * ```ts * import * as t from 'ts-fortress'; * * type EvenNumber = Readonly<{ type: 'even'; next: OddNumber | null }>; * * type OddNumber = Readonly<{ type: 'odd'; next: EvenNumber | null }>; * * // IMPORTANT: For mutual recursion, place terminal types (like nullType) * // first in unions, or provide explicit defaultValue * const EvenNumber: t.Type = t.recursion('EvenNumber', () => * t.record({ * type: t.literal('even'), * next: t.union([t.nullType, OddNumber]), // nullType first! * }), * ); * * const OddNumber: t.Type = t.recursion('OddNumber', () => * t.record({ * type: t.literal('odd'), * next: t.union([t.nullType, EvenNumber]), // nullType first! * }), * ); * ``` * * @example * // Alternative: Provide explicit defaultValue for mutual recursion * ```ts * import * as t from 'ts-fortress'; * * type EvenNumber = Readonly<{ type: 'even'; next: OddNumber | null }>; * * type OddNumber = Readonly<{ type: 'odd'; next: EvenNumber | null }>; * * const OddNumber: t.Type = t.recursion('OddNumber', () => * t.record({ * type: t.literal('odd'), * next: t.union([t.nullType, {} as t.Type]), * }), * ); * * const EvenNumber: t.Type = t.recursion( * 'EvenNumber', * () => * t.record({ * type: t.literal('even'), * next: t.union([OddNumber, t.nullType]), // Order doesn't matter * }), * { defaultValue: { type: 'even' as const, next: null } }, // Explicit default * ); * ``` * * @example * // Mutually recursive types with optional fields * ```ts * import * as t from 'ts-fortress'; * * type EvenNumber = Readonly<{ type: 'even'; next?: OddNumber }>; * * type OddNumber = Readonly<{ type: 'odd'; next?: EvenNumber }>; * * // When using optional fields in mutually recursive types, * // use forceUndefinedDefault to avoid infinite loops when accessing defaultValue * const EvenNumber: t.Type = t.recursion('EvenNumber', () => * t.record({ * type: t.literal('even'), * next: t.optional(OddNumber, { forceUndefinedDefault: true }), * }), * ); * * const OddNumber: t.Type = t.recursion('OddNumber', () => * t.record({ * type: t.literal('odd'), * next: t.optional(EvenNumber, { forceUndefinedDefault: true }), * }), * ); * ``` * * @remarks * **Important Notes:** * - The `definition` function is called lazily on first use, enabling self-reference * - For mutually recursive types, accessing `defaultValue` may cause infinite loops * unless proper precautions are taken: * 1. Place terminal types (like `nullType`) first in union types, OR * 2. Provide an explicit `defaultValue` in options, OR * 3. Use `optional()` with `forceUndefinedDefault: true` for optional fields * - The type definition itself (validation, type checking) works fine regardless of * union ordering or defaultValue specification */ export declare const recursion: (typeName: string, definition: () => Type, options?: Partial>) => Type; //# sourceMappingURL=recursion.d.mts.map