import type { IsLiteral, IsNever, ValueOf } from "type-fest"; // The primitive-union and tagged-union matchers differ in their universe, but the // handler/fallback plumbing is identical; these are the shared pieces. The // boundary is deliberate: `Handlers`, `Fallback` and `MustBePartial` stay with // each matcher because they are built from its universe. See development/library.md. // A handler: one universe member in, one return value out. export type UnaryFn = (shape: T) => R; // The primitive universe a matcher can discriminate. The primitive-union matcher // uses it directly; the tagged-union matcher uses it as the set of allowed // discriminant (`Tag`) values. `boolean` is admitted as the pair `true | false`; // see README § Caveats for the unsupported members. export type Matchable = string | number | boolean | null | undefined; // `boolean`, `null` and `undefined` cannot be property keys, so a mapped type // over a universe that includes one keys each such member by its // stringification. `Member` inverts that projection against the universe, so a // handler callback still receives the *real* member (`true`, not `"true"`). // Both matchers use the projection: the primitive-union matcher over its // universe, the tagged-union matcher over a discriminant property's values. See // README § Caveats for the limits. export type PatternKey = T extends boolean ? T extends true ? "true" : "false" : T extends null ? "null" : T extends undefined ? "undefined" : T; // The member(s) of `T` whose `PatternKey` is `K`: the universe-keyed inverse of // `PatternKey`. The key alone cannot recover the member (`"true"` and `true` // share it), so the handler parameter is derived from `T` instead. For a // supported (injective) universe the result is a single member. export type Member< T extends Matchable, K extends PropertyKey, > = T extends Matchable ? (PatternKey extends K ? T : never) : never; // The property key a `Matchable` member takes at runtime: booleans, `null` and // `undefined` stringify, and a numeric literal becomes its decimal string. export type Stringified = T extends boolean ? T extends true ? "true" : "false" : T extends null ? "null" : T extends undefined ? "undefined" : T extends number ? `${T}` : never; // The members of `T` that are also the stringification of another member, so // `PatternKey` cannot invert them. `never` means the universe is injective. export type Collisions = Extract>; // Why `T` is not a supported universe, or `never` when it is. A supported // universe is a finite union of literals with no value/stringification // collision: only then can `PatternKey` be inverted unambiguously. export type UnsupportedReason = IsLiteral> extends false ? "broad types like string, number and template literals are not supported" : [Collisions] extends [never] ? never : `a value and its stringification collide. Value is "${Collisions & string}"`; // The diagnostic for an unsupported universe. Extends `HandlerMap` so the // implementation's `handlers: HandlerMap` stays assignable when the gate is // intersected into a parameter; the property name is the message. export type UnsupportedUniverse = HandlerMap & Readonly>; // `unknown` for a supported universe (an intersection no-op), the diagnostic // otherwise. Intersecting rather than branching keeps `R` inference intact. export type UniverseGate = IsNever> extends true ? unknown : UnsupportedUniverse>; // `Extract` drops optional handlers (`undefined`) so `PatternReturns` also works // when `P`'s constraint has optional keys. export type PatternReturns

= ReturnType< Extract, (...args: never[]) => unknown> >; // The diagnostic raised when a fallback is supplied for an already-exhaustive // handler map. Each matcher's `MustBePartial` folds it into `Handled`'s // constraint so the guard is checked after inference. export interface RedundantFallback { readonly "every case is already handled, so the fallback is redundant": never; } // The runtime dispatch map the handler maps and fallback erase to. export type HandlerMap = Record< string | number, UnaryFn | undefined >;