import type { Exact } from "type-fest"; import type { HandlerMap, Matchable, PatternKey, PatternParam, PatternReturns, RedundantFallback, UnaryFn, } from "./matcher-shared.ts"; type Handlers = { [K in PatternKey]: UnaryFn, R>; }; // The fallback is a *second argument*, not a property of the handler map, // because its parameter is the remainder `Exclude` and TypeScript // fixes a property's contextual type before it infers its sibling keys. A later // argument, by contrast, is contextually typed from inference on an earlier // one, so the split is what makes the remainder expressible at all. // See development/library.md. type Fallback = UnaryFn< Exclude>, R >; // A fallback is redundant once the handler map covers `T`. The guard is folded // into `Handled`'s own (self-referential) constraint so it is checked *after* // inference; a conditional in the fallback's parameter type is evaluated while // `Handled` is still its constraint and would reject context-sensitive partial // maps. That placement also fixes where the diagnostic lands: the constraint // failure is reported on the argument that inferred `Handled` (the handler // map), so the required property is spelled as the message instead of relying // on its position. See development/library.md. type MustBePartial = PatternKey extends keyof Handled ? RedundantFallback : unknown; // TypeScript does not apply the excess-property check to a generic constraint, // so `Exact` restores it for the generic forms: a handler map can otherwise // carry keys outside `T`. // Strict returns: one common `R`. Overload order is load-bearing: // #1 Handlers (first) -> the exhaustive form and the autocomplete popup // #2 Fallback (last) -> accepts a partial handler map plus a fallback interface PrimitiveUnionMatcherStrict { (handlers: Handlers): UnaryFn; < R, Handled extends Exact>, Handled> & MustBePartial, >( handlers: Handled & Partial>, fallback: Fallback, ): UnaryFn; } // Widened returns: the union of every handler's return type. `P` is inferred // from the whole handler map, whose closed constraint supplies the // contextual/autocomplete type. interface PrimitiveUnionMatcherWidening {

, P>>( handlers: P, ): UnaryFn>; < R, Handled extends Exact>, Handled> & MustBePartial, >( handlers: Handled, fallback: Fallback, ): UnaryFn | R>; } const dispatch = (handlers: HandlerMap, fallback?: UnaryFn) => (shape: Matchable): unknown => // `handlers[true]` already coerces to the `"true"` property at runtime, // identical to `handlers[String(shape)]`, so indexing with `shape` // directly is sound: `shape` is a facade-checked universe member and // `PatternKey` only ever produces valid property keys. The assertion is // needed solely because TypeScript forbids indexing with // `boolean`/`null`/`undefined` (TS2538); it buys the number fast path. ( handlers[ // oxlint-disable-next-line typescript/no-unsafe-type-assertion shape as string | number ] ?? fallback ?? (() => { throw new Error(`Unhandled shape: ${String(shape)}`); }) )( // oxlint-disable-next-line typescript/no-unsafe-type-assertion shape as never, ); export const getPrimitiveUnionMatcher = < T extends Matchable, >(): PrimitiveUnionMatcherStrict => dispatch; export const getPrimitiveUnionMatcherW = < T extends Matchable, >(): PrimitiveUnionMatcherWidening => dispatch;