-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
TypeScript Version: 3.7.0-dev.20190930
Search Terms: union never switch
Code
Compare the type of the switch
argument at the default:
branch of the three switch
es below:
const a = "foo"
switch (a) {
case "foo": break;
default:
a // never
}
const b = {
discriminant: "foo" as const
} as { discriminant: "foo" } | { discriminant: "bar" }
switch (b.discriminant){
case "foo": break;
case "bar": break;
default:
b // never
}
const c = {
discriminant: "foo" as const
} as { discriminant: "foo" }
switch (c.discriminant){
case "foo": break;
default:
c // object, but was expecting never
}
Expected behavior:
c
should be never
to be consistent with a
and b
.
Actual behavior:
c
is still the type { discriminant: "foo" }
.
Playground Link: here
Context
I have this type-guarding function that acts as an identity function on the second argument but requires that the first argument is never
.
export function unreachable<T>(never: never, result: T): T {
return result;
}
I then use this to create a throw
statement at the end of a switch
that will type-error if I expanded the union that went into the switch and neglected to implement a branch that handles the new subtype. This allows me to perform a run-time assertion of an unsupported option and check that I have implemented all supported options in one line.
switch (union.discriminant) {
case "foo": return doThis();
case "bar": return doThat();
default:
throw Error(unreachable(union, "Unrecognized union type."));
It works beautifully until the union is not not actually a union at all (see case c
in the example above). This ruined my ability to keep the switch
form plus unreachable
generically regardless of whether the switch argument is a discriminated union or just a simple object. The thing is, I run into situations all the time where the switch will first art with one discriminant value and grow later. But at any point in time I want to be able to capture both the run-time error and the compile-time check that all valid options have been exhausted. This includes when there is only one case on the switch
.
Therefore I would find it useful that the behavior between c
and b
above are consistent. That way, I can use the switch boilerplate starting with the non-union and then expand it as I go.
Related Issues:
This seems to be related (but still somewhat different) to #20375 and #30557. I'm fine if this is unified into an existing issue as the essence of this issue remains addressed.