✨ Reject a fallback for an exhaustive map
A fallback paired with a handler map that already covers `T` was still accepted, even though its remainder is empty. Fold the guard into `Handled`'s own (F-bounded) constraint so it is checked after inference; a conditional in the fallback parameter is evaluated while `Handled` is still its constraint and rejects every partial map whose handler callbacks need contextual typing.
This commit is contained in:
1 parent
7e7f716424
commit
f27966e803
5 files changed
+65
-14
No files matched your search
+25
-2
@@ -21,6 +21,21 @@ type Fallback<T extends string | number, Handled, R> = UnaryFn<
|
||||
R
|
||||
>;
|
||||
|
||||
// A fallback is redundant once the handler map covers `T`. The guard is folded
|
||||
// into `Handled`'s own (self-referential) constraint so it is checked *after*
|
||||
// inference; a conditional in the fallback's parameter type is evaluated while
|
||||
// `Handled` is still its constraint and would reject context-sensitive partial
|
||||
// maps. That placement also fixes where the diagnostic lands: the constraint
|
||||
// failure is reported on the argument that inferred `Handled` (the handler
|
||||
// map), so the required property is spelled as the message instead of relying
|
||||
// on its position. See development/library.md.
|
||||
interface RedundantFallback {
|
||||
readonly "every case is already handled, so the fallback is redundant": never;
|
||||
}
|
||||
type MustBePartial<T extends string | number, Handled> = T extends keyof Handled
|
||||
? RedundantFallback
|
||||
: unknown;
|
||||
|
||||
// TypeScript does not apply the excess-property check to a generic constraint,
|
||||
// so `Exact` restores it for the generic forms: a handler map can otherwise
|
||||
// carry keys outside `T`.
|
||||
@@ -31,7 +46,11 @@ type Fallback<T extends string | number, Handled, R> = UnaryFn<
|
||||
// #2 Fallback (last) -> accepts a partial handler map plus a fallback
|
||||
interface MatcherStrict<T extends string | number> {
|
||||
<R>(handlers: Handlers<T, R>): UnaryFn<T, R>;
|
||||
<R, Handled extends Exact<Partial<Handlers<T, R>>, Handled>>(
|
||||
<
|
||||
R,
|
||||
Handled extends Exact<Partial<Handlers<T, R>>, Handled> &
|
||||
MustBePartial<T, Handled>,
|
||||
>(
|
||||
handlers: Handled & Partial<Handlers<T, R>>,
|
||||
fallback: Fallback<T, Handled, R>,
|
||||
): UnaryFn<T, R>;
|
||||
@@ -46,7 +65,11 @@ interface MatcherWidening<T extends string | number> {
|
||||
<P extends Exact<Handlers<T, unknown>, P>>(
|
||||
handlers: P,
|
||||
): UnaryFn<T, PatternReturns<P>>;
|
||||
<R, Handled extends Exact<Partial<Handlers<T, unknown>>, Handled>>(
|
||||
<
|
||||
R,
|
||||
Handled extends Exact<Partial<Handlers<T, unknown>>, Handled> &
|
||||
MustBePartial<T, Handled>,
|
||||
>(
|
||||
handlers: Handled,
|
||||
fallback: Fallback<T, Handled, R>,
|
||||
): UnaryFn<T, PatternReturns<Handled> | R>;
|
||||
|
||||
Reference in new issue
Block a user