/** * @file Advanced Pattern Matching for State Machines * @description * Provides type-safe pattern matching utilities for discriminating between machine types. * Supports three complementary APIs: type guards, exhaustive pattern matching, and simple matching. * * @example * ```typescript * // Define a matcher for class-based machines * const match = createMatcher( * classCase('idle', IdleMachine), * classCase('loading', LoadingMachine), * classCase('success', SuccessMachine) * ); * * // API 1: Type Guards * if (match.is.loading(machine)) { * // machine is narrowed to LoadingMachine * } * * // API 2: Exhaustive Pattern Matching * const result = match.when(machine).is( * match.case.idle(() => 'idle'), * match.case.loading(() => 'loading'), * match.case.success(m => m.context.data), * match.exhaustive * ); * * // API 3: Simple Match * const name = match(machine); // 'idle' | 'loading' | 'success' | null * ``` */ import type { Machine, Context } from './index'; /** * A matcher case tuple that defines a state pattern. * * @template Name - The unique name for this case (used for type guards and pattern matching) * @template M - The machine type this case matches * @template Pred - The predicate function that determines if a machine matches this case */ export type MatcherCase m is M> = readonly [ name: Name, machineType: M, predicate: Pred ]; /** * Extracts the machine type from a MatcherCase. */ type CaseToMachine = C extends MatcherCase ? M : never; /** * Extracts the case name from a MatcherCase. */ type CaseToName = C extends MatcherCase ? Name : never; /** * Builds a mapping from case names to their machine types. * @typeParam Cases - Matcher-case tuple to index by name. */ export type CasesToMapping[]> = { [C in Cases[number] as CaseToName]: CaseToMachine; }; /** * Creates a union of all possible machine types from the cases. * @typeParam Cases - Matcher-case tuple to combine. */ export type MatcherUnion[]> = Cases[number] extends MatcherCase ? M : never; /** * Extracts the union of all case names. * @typeParam Cases - Matcher-case tuple to inspect. */ export type CaseNames[]> = CaseToName; /** * A branded type representing a case handler in pattern matching. * This is used internally to track which cases have been handled. * * @typeParam Name - Case name handled by this value. * @typeParam M - Narrowed machine received by the handler. * @typeParam R - Handler result type. */ export type CaseHandler = { readonly __brand: 'CaseHandler'; readonly __name: Name; readonly __machine: M; readonly __return: R; readonly handler: (machine: M) => R; }; /** * Exhaustiveness marker - signals that all cases must be handled. */ export type ExhaustivenessMarker = { readonly __exhaustive: true; }; /** * Extracts machine types from an array of case handlers. */ type ExtractHandledMachines = H extends readonly [infer First, ...infer Rest] ? (First extends CaseHandler ? M : never) | ExtractHandledMachines : never; /** * Extracts return types from an array of case handlers. */ type ExtractHandlerReturn = H extends readonly CaseHandler[] ? R : never; /** * Checks if all machine types in Union have been handled. * Returns true if exhaustive, otherwise returns an error type with missing cases. * * @typeParam Union - Complete machine union expected by the match. * @typeParam Handled - Machine members represented by handlers. */ export type IsExhaustive = Exclude extends never ? true : { readonly __error: 'Non-exhaustive match - missing cases'; readonly __missing: Exclude; }; /** * Pattern matching builder returned by matcher.when(). * * @typeParam _Cases - Cases registered on the parent matcher. * @typeParam M - Runtime value being matched. */ export interface WhenBuilder<_Cases extends readonly MatcherCase[], M> { /** * Execute pattern matching with exhaustiveness checking. * * @template R - The return type of all handlers * @param handlers - Array of case handlers followed by exhaustiveness marker * @returns The result of the matched handler, or compile error if not exhaustive * * @example * ```typescript * match.when(machine).is( * match.case.idle(() => 'idle'), * match.case.loading(() => 'loading'), * match.exhaustive * ); * ``` */ /** * Overload 1: Infer return type from handlers (Enables exhaustiveness checking). */ is, any, any>[]>(...handlers: [...H, ExhaustivenessMarker]): IsExhaustive> extends true ? ExtractHandlerReturn : IsExhaustive>; /** * Overload 2: Explicit return type (No exhaustiveness checking). */ is(...handlers: [...CaseHandler, any, R>[], ExhaustivenessMarker]): R; } /** * The main Matcher interface with three APIs. * * @typeParam Cases - Registered matcher-case tuple. */ export interface Matcher[]> { /** * API 1: Type guard access via dynamic properties. * * @example * ```typescript * if (match.is.loading(machine)) { * // machine is narrowed to LoadingMachine * } * ``` */ readonly is: { [Name in CaseNames]: (machine: any) => machine is CasesToMapping[Name]; }; /** * API 2a: Pattern matching builder. * * @example * ```typescript * match.when(machine).is( * match.case.idle(() => 'idle'), * match.case.loading(() => 'loading'), * match.exhaustive * ); * ``` */ when: (machine: M) => WhenBuilder; /** * API 2b: Case handler creator for pattern matching. * * @example * ```typescript * match.case.loading((m) => `Loading: ${m.context.startTime}`) * ``` */ readonly case: { [Name in CaseNames]: (handler: (machine: CasesToMapping[Name]) => R) => CaseHandler[Name], R>; }; /** * API 2c: Exhaustiveness marker for pattern matching. */ readonly exhaustive: ExhaustivenessMarker; /** * API 3: Simple match - returns the name of the matched case or null. * * @example * ```typescript * const name = match(machine); // 'idle' | 'loading' | 'success' | null * ``` */ (machine: M): M extends MatcherUnion ? CaseNames | null : null; } /** * Creates a type-safe matcher for discriminating between machine types. * * @template Cases - Tuple of [name, MachineType, predicate] configurations * @param cases - Array of matcher case definitions * @returns A matcher object with three APIs: is (type guards), when (pattern matching), and direct call (simple match) * * @example * ```typescript * // Class-based matching * const match = createMatcher( * ['idle', IdleMachine, (m): m is IdleMachine => m instanceof IdleMachine], * ['loading', LoadingMachine, (m): m is LoadingMachine => m instanceof LoadingMachine] * ); * * // Or use helper functions * const match = createMatcher( * classCase('idle', IdleMachine), * classCase('loading', LoadingMachine) * ); * ``` */ export declare function createMatcher m is any>[]>(...cases: Cases): Matcher; /** * Creates a class-based matcher case using instanceof checking. * This is the most common pattern for Type-State machines. * * @template Name - The unique name for this case * @template T - The class constructor * @param name - The name to use for this case * @param machineClass - The class to check with instanceof * @returns A matcher case tuple * * @example * ```typescript * const match = createMatcher( * classCase('idle', IdleMachine), * classCase('loading', LoadingMachine), * classCase('success', SuccessMachine) * ); * ``` */ export declare function classCase any>(name: Name, machineClass: T): MatcherCase, (m: any) => m is InstanceType>; /** * Creates a discriminated union matcher case based on a context property. * This integrates with the existing hasState utility for context-based discrimination. * * @template Name - The unique name for this case * @template M - The machine type (use Machine for proper narrowing) * @template K - The context key to check * @template V - The value to match * @param name - The name to use for this case * @param key - The context property to check * @param value - The value the property should equal * @returns A matcher case tuple * * @example * ```typescript * type FetchContext = * | { status: 'idle' } * | { status: 'loading' } * | { status: 'success'; data: string }; * * const match = createMatcher( * discriminantCase<'idle', Machine, 'status', 'idle'>('idle', 'status', 'idle'), * discriminantCase<'loading', Machine, 'status', 'loading'>('loading', 'status', 'loading'), * discriminantCase<'success', Machine, 'status', 'success'>('success', 'status', 'success') * ); * ``` */ export declare function discriminantCase = Machine, K extends keyof Context = any, V extends Context[K] = any>(name: Name, key: K, value: V): MatcherCase, { [P in K]: V; }>; }, (m: M) => m is M & { context: Extract, { [P in K]: V; }>; }>; /** * Creates a custom matcher case with a user-defined predicate. * For advanced matching logic beyond instanceof or discriminants. * * @template Name - The unique name for this case * @template M - The machine type this case matches (inferred from predicate) * @param name - The name to use for this case * @param predicate - A type guard function that determines if a machine matches * @returns A matcher case tuple * * @example * ```typescript * const match = createMatcher( * customCase('complex', (m): m is ComplexMachine => { * return m.context.value > 10 && m.context.status === 'active'; * }) * ); * ``` */ export declare function customCase(name: Name, predicate: (m: any) => m is M): MatcherCase m is M>; /** * Creates a discriminated matcher builder for a specific context union type. * Provides better type inference by capturing the context type upfront. * * @template C - The discriminated union context type * @returns A builder object with a `case` method for defining cases with less boilerplate * * @example * ```typescript * type FetchContext = * | { status: 'idle' } * | { status: 'loading'; startTime: number } * | { status: 'success'; data: string }; * * const builder = forContext(); * * const match = createMatcher( * builder.case('idle', 'status', 'idle'), * builder.case('loading', 'status', 'loading'), * builder.case('success', 'status', 'success') * ); * * // Full type inference and narrowing works! * if (match.is.success(machine)) { * console.log(machine.context.data); // ✓ TypeScript knows data exists * } * ``` */ export declare function forContext(): { /** * Creates a discriminated union case with full type inference. */ case(name: Name, key: K, value: V): MatcherCase; }, (m: { readonly context: C; }) => m is { readonly context: Extract; }>; }; export {}; //# sourceMappingURL=matcher.d.ts.map