✨ Allow boolean and nullish tags in tagged unions
A `boolean` / `null` / `undefined` discriminant cannot key a handler map, so the tagged-union matcher now routes it through the `PatternKey` / `PatternParam` projection the primitive-union matcher already used. Both matchers share the projection from `matcher-shared.ts`; `MapTaggedUnion`, `Handlers`, `HandledMembers` and `MustBePartial` key off the projected form and invert it to recover the real member. Covers exhaustive and fallback dispatch, the redundant-fallback guard, and the LSP popup offering the tags by name.
This commit is contained in:
1 parent
b81a8149d9
commit
e96ca12149
5 files changed
+243
-52
No files matched your search
+20
-12
@@ -97,22 +97,25 @@ Each factory is two overloads whose order is load-bearing:
|
||||
|
||||
#### Decision (2026-09)
|
||||
|
||||
`src/matcher-shared.ts` holds the four universe-agnostic pieces both matchers
|
||||
use: `UnaryFn`, `PatternReturns`, `RedundantFallback`, `HandlerMap`.
|
||||
`src/matcher-shared.ts` holds the six universe-agnostic pieces both matchers
|
||||
use: `UnaryFn`, `PatternReturns`, `RedundantFallback`, `HandlerMap`, and the
|
||||
`PatternKey` / `PatternParam` key projection.
|
||||
|
||||
#### Why
|
||||
|
||||
- `RedundantFallback`'s property name is the diagnostic, so one definition
|
||||
keeps the two matchers' message from drifting; the other three appear verbatim
|
||||
in both public signatures.
|
||||
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.
|
||||
|
||||
#### Rejected
|
||||
|
||||
- **A generic `Matcher<Universe>` over the interface pair, `Handlers`,
|
||||
`Fallback` and `MustBePartial`.** Each is built from its own universe
|
||||
(`PatternKey`/`PatternParam` vs `Tags`/`MapTaggedUnion`); abstracting over the
|
||||
(`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.
|
||||
contextual typing it exists to preserve. The `PatternKey` / `PatternParam`
|
||||
projection is the one piece both universes genuinely share.
|
||||
|
||||
## Tagged-union matcher
|
||||
|
||||
@@ -152,15 +155,18 @@ The key is a separate call because `K` is inferred from its literal argument and
|
||||
assertion, the tagged twin of the primitive-union dispatch's `shape as string | number`.
|
||||
- **`MapTaggedUnion` distributes with `Extract`.** A duplicated tag yields a
|
||||
union of members instead of dropping one.
|
||||
- **A `boolean` / `null` / `undefined` tag goes through the shared
|
||||
`PatternKey` / `PatternParam` projection.** `Discriminated` admits those tags
|
||||
(they are in `Tag`), but they cannot key a mapped type, so the handler map is
|
||||
keyed by the stringified form (`true` → `"true"`) and `PatternParam` inverts
|
||||
it to recover the member. This is the same projection the primitive-union
|
||||
matcher uses over its universe, which is why it lives in `matcher-shared.ts`.
|
||||
|
||||
#### Known issue
|
||||
|
||||
- Tags are `string | number` only. A `boolean` / `null` / `undefined`
|
||||
discriminant (`{ ok: true } | { ok: false }`) is rejected by `Discriminated`,
|
||||
because those values are not property keys; supporting them needs the
|
||||
primitive-union matcher's `PatternKey` / `PatternParam` projection.
|
||||
- A member's tag must be unique across the union; two members with the same tag
|
||||
collapse to a union under one handler.
|
||||
collapse to a union under one handler. The same holds for a tag colliding with
|
||||
its stringification (`true | "true"`) — see [README § Caveats](../README.md#caveats).
|
||||
|
||||
## Primitive universe
|
||||
|
||||
@@ -172,7 +178,9 @@ with `boolean` admitted as `true | false`.
|
||||
`boolean`/`null`/`undefined` are not property keys, so handler-map keys are a
|
||||
projection (`PatternKey`: each member stringified) and `PatternParam` inverts
|
||||
it, so callbacks receive the real member (`true`, not `"true"`). The popup
|
||||
offers `true`, `false`, `null`, `undefined` by name (verified over LSP).
|
||||
offers `true`, `false`, `null`, `undefined` by name (verified over LSP). The
|
||||
same projection is shared with the tagged-union matcher; see
|
||||
§ Tagged-union matcher.
|
||||
|
||||
#### Why
|
||||
|
||||
|
||||
Reference in new issue
Block a user