All files primitive.ts

100% Statements 63/63
100% Branches 6/6
100% Functions 3/3
100% Lines 63/63

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 641x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 15x 32x 1x 1x 1x 1x 1x 1x  
import type { ValueOf } from "type-fest";
 
type UnaryFn<T, R> = (shape: T) => R;
 
// `Extract` drops optional handlers (`undefined`) so `PatternReturns` also works
// when `P`'s constraint has optional keys.
type PatternReturns<P> = ReturnType<
    Extract<ValueOf<P>, (...args: never[]) => unknown>
>;
 
type Handlers<R, T extends string | number> = { [K in T]: UnaryFn<K, R> };
 
// Fallback form: `_` is a required key, the T-keys are optional.
type Fallback<R, T extends string | number> = Partial<Handlers<R, T>> & {
    _: UnaryFn<T, R>;
};
 
// Completion form: T-keys required, `_` optional. Overload #1, because
// TypeScript takes the *first* overload as the contextual type for the popup.
type ExhaustiveLoose<R, T extends string | number> = Handlers<R, T> & {
    _?: UnaryFn<T, R>;
};
 
// oxlint-disable typescript/unified-signatures
// Strict returns: one common `R`. Overload order is load-bearing:
//   #1 ExhaustiveLoose -> autocomplete `_?, a, b`
//   #2 Fallback        -> accepts a partial pattern
//   #3 Handlers (last) -> "Property 'b' is missing" is the reported error
interface MatcherStrict<T extends string | number> {
    <R>(pattern: ExhaustiveLoose<R, T>): UnaryFn<T, R>;
    <R>(pattern: Fallback<R, T>): UnaryFn<T, R>;
    <R>(pattern: Handlers<R, T>): UnaryFn<T, R>;
}
// oxlint-enable typescript/unified-signatures
 
// Widened returns: the union of every handler's return type. `P` is inferred
// from the whole parameter, whose closed constraint supplies the
// contextual/autocomplete type; the `keyof P` guard appended to each overload
// rejects keys outside `T`/`_`.
interface MatcherWidening<T extends string | number> {
    <P extends ExhaustiveLoose<unknown, T>>(
        pattern: P & (keyof P extends T | "_" ? unknown : never),
    ): UnaryFn<T, PatternReturns<P>>;
    <P extends Fallback<unknown, T>>(
        pattern: P & (keyof P extends T | "_" ? unknown : never),
    ): UnaryFn<T, PatternReturns<P>>;
    <P extends Handlers<unknown, T>>(
        pattern: P & (keyof P extends T | "_" ? unknown : never),
    ): UnaryFn<T, PatternReturns<P>>;
}
 
type HandlerMap = Record<string | number, UnaryFn<never, unknown> | undefined>;
 
const dispatch =
    (pattern: HandlerMap) =>
    (shape: string | number): unknown =>
        // oxlint-disable-next-line typescript/no-non-null-assertion typescript/no-unsafe-type-assertion
        (pattern[shape] ?? pattern["_"]!)(shape as never);
 
export const getMatcher = <T extends string | number>(): MatcherStrict<T> =>
    dispatch;
export const getMatcherW = <T extends string | number>(): MatcherWidening<T> =>
    dispatch;