Repository navigation
What's new 🚀
Classify future discriminant values with an explicit fallback
discriminatedUnionWithFallback adds an explicit fallback path for discriminated unions whose input may contain variants that the current application does not know about yet.
This is useful at open boundaries such as:
- versioned APIs that may introduce new event types
- webhook or event-stream consumers
- plugin or protocol messages
- AST or schema versions where newer node kinds may appear
Known variants are still validated exhaustively with their existing guards. If an input uses a known discriminant but has an invalid shape, validation fails as usual.
Only an undeclared discriminant reaches the fallback factory.
The fallback is then transformed into one canonical variant, keeping the resulting union closed enough for an exhaustive switch.
import {
discriminatedUnionWithFallback,
isNumber,
oneOfValues,
typedStruct
} from 'is-kit';
type Event =
| { kind: 'click'; x: number; y: number }
| { kind: 'scroll'; delta: number };
const classifyEvent = discriminatedUnionWithFallback<Event>()(
'kind',
['click', 'scroll'],
{
click: typedStruct<Extract<Event, { kind: 'click' }>>()({
kind: oneOfValues('click'),
x: isNumber,
y: isNumber
}),
scroll: typedStruct<Extract<Event, { kind: 'scroll' }>>()({
kind: oneOfValues('scroll'),
delta: isNumber
})
},
{
tag: 'unknown',
create: (input, originalKind) => ({
input,
originalKind
})
}
);
const result = classifyEvent({
kind: 'hover',
target: 'button'
});
if (result.valid) {
switch (result.value.kind) {
case 'click':
result.value.x;
break;
case 'scroll':
result.value.delta;
break;
case 'unknown':
result.value.originalKind; // 'hover'
result.value.input;
break;
}
}For an undeclared input such as
{ kind: 'hover', target: 'button' }the decoder produces a canonical fallback variant
{
kind: 'unknown',
originalKind: 'hover',
input: { kind: 'hover', target: 'button' }
}This differs from silently accepting an open union, downstream code still sees a finite set of variants click, scroll, or unknown.
A malformed known branch does not fall through to unknown
classifyEvent({
kind: 'click',
x: 'invalid',
y: 24
});
// { valid: false }The fallback factory can also reject an undeclared variant explicitly by returning undefined.
const classifyEvent = discriminatedUnionWithFallback<Event>()(
'kind',
['click', 'scroll'],
guards,
{
tag: 'unknown',
create: (_input, originalKind) =>
originalKind === 'experimental'
? undefined
: { originalKind }
}
);Because fallback handling transforms the original input into a new canonical variant,
discriminatedUnionWithFallback returns a ParseResult decoder rather than a type guard. 👍
What's Changed
- fix(ci): allow freshly published JSR smoke tests by @nyaomaru in #344
- docs(changelog): 1.15.3 by @github-actions[bot] in #343
- fix: reject invalid struct schema fields by @nyaomaru in #346
- perf: iterate array guards by index by @nyaomaru in #347
- test: add packed package compatibility matrix by @nyaomaru in #348
- fix(docs): use dedicated OGP image by @nyaomaru in #350
- feat: add explicit fallback variants for discriminated union by @nyaomaru in #349
- Release: 1.16.0 by @github-actions[bot] in #351
Full Changelog: v1.15.3...v1.16.0