♻️ Name the matcher factories getMatcher / getMatcherW
Drop the strict/widened aliases from src/index.ts so the factories are exported under their own names, and align the type tests, the autocomplete probe (which now imports the public surface from ./index.ts) and library.md with them. Also correct the widened-overload comment: the excess-property check does not apply to a generic P, so MatcherWidening's keyof guard — not the closed constraint — is what rejects keys outside T/_, as library.md already documented.
This commit is contained in:
1 parent
05fad0fcd6
commit
800c11139d
5 files changed
+69
-84
No files matched your search
+17
-33
@@ -21,35 +21,27 @@ type ExhaustiveLoose<R, T extends string | number> = Handlers<R, T> & {
|
||||
_?: UnaryFn<T, R>;
|
||||
};
|
||||
|
||||
// The `P`-constraints are deliberately *closed* (no index signature): that is
|
||||
// what makes the excess-property check reject keys outside `T`/`_`.
|
||||
type ExhaustiveShape<T extends string | number> = Handlers<unknown, T> & {
|
||||
_?: UnaryFn<T, unknown>;
|
||||
};
|
||||
type FallbackShape<T extends string | number> = Partial<
|
||||
Handlers<unknown, T>
|
||||
> & {
|
||||
_: UnaryFn<T, unknown>;
|
||||
};
|
||||
|
||||
// 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 StrictFn<T extends string | number> {
|
||||
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 the whole
|
||||
// parameter (not an intersection), so its closed constraint both supplies the
|
||||
// contextual/autocomplete type and enforces the excess-property check.
|
||||
interface WidenedFn<T extends string | number> {
|
||||
<P extends ExhaustiveShape<T>>(
|
||||
// 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 FallbackShape<T>>(
|
||||
<P extends Fallback<unknown, T>>(
|
||||
pattern: P & (keyof P extends T | "_" ? unknown : never),
|
||||
): UnaryFn<T, PatternReturns<P>>;
|
||||
<P extends Handlers<unknown, T>>(
|
||||
@@ -61,19 +53,11 @@ type HandlerMap = Record<string | number, UnaryFn<never, unknown> | undefined>;
|
||||
|
||||
const dispatch =
|
||||
(pattern: HandlerMap) =>
|
||||
(shape: string | number): unknown => {
|
||||
const handler = pattern[shape] ?? pattern["_"];
|
||||
return handler === undefined
|
||||
? undefined
|
||||
: Reflect.apply(handler, undefined, [shape]);
|
||||
};
|
||||
(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 strict =
|
||||
<T extends string | number>(): StrictFn<T> =>
|
||||
(pattern: HandlerMap) =>
|
||||
dispatch(pattern);
|
||||
|
||||
export const widened =
|
||||
<T extends string | number>(): WidenedFn<T> =>
|
||||
(pattern: HandlerMap) =>
|
||||
dispatch(pattern);
|
||||
export const getMatcher = <T extends string | number>(): MatcherStrict<T> =>
|
||||
dispatch;
|
||||
export const getMatcherW = <T extends string | number>(): MatcherWidening<T> =>
|
||||
dispatch;
|
||||
Reference in new issue
Block a user