♻️ Share the Matchable universe between both matchers
This commit is contained in:
1 parent
d88a4eeb45
commit
6228b4922d
4 files changed
+24
-17
No files matched your search
@@ -97,9 +97,9 @@ Each factory is two overloads whose order is load-bearing:
|
||||
|
||||
#### Decision (2026-09)
|
||||
|
||||
`src/matcher-shared.ts` holds the six universe-agnostic pieces both matchers
|
||||
use: `UnaryFn`, `PatternReturns`, `RedundantFallback`, `HandlerMap`, and the
|
||||
`PatternKey` / `PatternParam` key projection.
|
||||
`src/matcher-shared.ts` holds the seven universe-agnostic pieces both matchers
|
||||
use: `UnaryFn`, `PatternReturns`, `RedundantFallback`, `HandlerMap`, the shared
|
||||
`Matchable` universe, and the `PatternKey` / `PatternParam` key projection.
|
||||
|
||||
#### Why
|
||||
|
||||
@@ -107,6 +107,10 @@ use: `UnaryFn`, `PatternReturns`, `RedundantFallback`, `HandlerMap`, and the
|
||||
keeps the two matchers' message from drifting; the other pieces appear
|
||||
verbatim in both public signatures or are the same projection over each
|
||||
matcher's universe.
|
||||
- **`Matchable` is one definition, not two.** The primitive-union matcher's
|
||||
universe and the tagged-union matcher's allowed `Tag` values are the same set,
|
||||
so aliasing them keeps the two matchers from drifting apart on what they
|
||||
accept (`symbol`/`bigint` rejected once).
|
||||
|
||||
#### Rejected
|
||||
|
||||
@@ -114,8 +118,8 @@ use: `UnaryFn`, `PatternReturns`, `RedundantFallback`, `HandlerMap`, and the
|
||||
`Fallback` and `MustBePartial`.** Each is built from its own universe
|
||||
(`Tags`/`MapTaggedUnion` vs the primitive values); abstracting over the
|
||||
F-bounded `Handled` constraint that makes the remainder work risks the
|
||||
contextual typing it exists to preserve. The `PatternKey` / `PatternParam`
|
||||
projection is the one piece both universes genuinely share.
|
||||
contextual typing it exists to preserve. `Matchable` and the `PatternKey` /
|
||||
`PatternParam` projection are the pieces both universes genuinely share.
|
||||
|
||||
## Tagged-union matcher
|
||||
|
||||
|
||||
@@ -8,6 +8,12 @@ import type { ValueOf } from "type-fest";
|
||||
// A handler: one universe member in, one return value out.
|
||||
export type UnaryFn<T, R> = (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. `PatternParam` inverts that projection, so a handler callback
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Exact } from "type-fest";
|
||||
|
||||
import type {
|
||||
HandlerMap,
|
||||
Matchable,
|
||||
PatternKey,
|
||||
PatternParam,
|
||||
PatternReturns,
|
||||
@@ -9,10 +10,6 @@ import type {
|
||||
UnaryFn,
|
||||
} from "./matcher-shared.ts";
|
||||
|
||||
// The primitive universe a matcher can discriminate. `boolean` is admitted as
|
||||
// the pair `true | false`; see README § Caveats for the unsupported members.
|
||||
type Matchable = string | number | boolean | null | undefined;
|
||||
|
||||
type Handlers<T extends Matchable, R> = {
|
||||
[K in PatternKey<T>]: UnaryFn<PatternParam<K>, R>;
|
||||
};
|
||||
|
||||
+8
-8
@@ -2,6 +2,7 @@ import type { Exact, UnknownRecord } from "type-fest";
|
||||
|
||||
import type {
|
||||
HandlerMap,
|
||||
Matchable,
|
||||
PatternKey,
|
||||
PatternParam,
|
||||
PatternReturns,
|
||||
@@ -9,22 +10,21 @@ import type {
|
||||
UnaryFn,
|
||||
} from "./matcher-shared.ts";
|
||||
|
||||
// A tagged union is discriminated by one property whose values are the tags.
|
||||
// `string` and `number` tags key a handler map directly; `boolean`, `null` and
|
||||
// `undefined` are admitted too but are not property keys, so they go through the
|
||||
// `PatternKey` projection. `symbol` has no literal syntax to write a handler
|
||||
// under, and `bigint` is not a property key.
|
||||
type Tag = string | number | boolean | null | undefined;
|
||||
// A tagged union is discriminated by one property whose values are the tags (a
|
||||
// `Matchable`). `string` and `number` tags key a handler map directly;
|
||||
// `boolean`, `null` and `undefined` are admitted too but are not property keys,
|
||||
// so they go through the `PatternKey` projection. `symbol` has no literal syntax
|
||||
// to write a handler under, and `bigint` is not a property key.
|
||||
|
||||
// The discriminant values of `T` under `K`. `Extract` keeps the finite literal
|
||||
// tags and leaves a widened `string`/`number` as itself, so an open universe
|
||||
// keeps an open fallback.
|
||||
type Tags<T extends object, K extends keyof T> = Extract<T[K], Tag>;
|
||||
type Tags<T extends object, K extends keyof T> = Extract<T[K], Matchable>;
|
||||
|
||||
// The keys of `T` that can act as a discriminant. `getTaggedUnionMatcher<T>()`
|
||||
// accepts only these, so the factory rejects a key whose values are not tags.
|
||||
type Discriminated<T extends object> = {
|
||||
[K in keyof T]: T[K] extends Tag ? K : never;
|
||||
[K in keyof T]: T[K] extends Matchable ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
// The member(s) of `T` tagged `V`. `Extract` distributes over the union, so a
|
||||
|
||||
Reference in new issue
Block a user