Issue
I have the following oversimplified code snippet:
type a = 'a' | 'b';
const fn = <T extends a>(param: T): T => {
switch(param) {
case 'a':
return 'a' as T;
case 'b':
return 'b' as T;
}
};
I cannot figure out, why does the compiler complain about the lack of return, and is there a way to fix it that is future proof (e.g. not adding a default case, I wanna make sure all cases are explicitly handled, so if in the future the type is extended, I do want it to fail)
it works when I remove the generic, but in my scenario, the return type is generic on T
Solution
This is currently a missing feature of TypeScript, reported at microsoft/TypeScript#13215. Generics and narrowing don't work together very well; if you want to get exhaustiveness checking from switch
/case
statements, then you'll need the relevant value to be a union type directly and not a generic type constrained to a union. Maybe someday TypeScript will automatically apply exhaustiveness checks for generics, but for now it's not part of the language.
For the example code as given, the simplest approach is to widen param
from T
to "a" | "b"
when doing the check:
type AB = 'a' | 'b';
const fn = <T extends AB>(param: T): T => { // okay
const p: AB = param; // <-- widen
switch (p) {
case 'a':
return 'a' as T;
case 'b':
return 'b' as T;
}
};
You could also use the satisfies
operator with a type assertion to safely widen a value without copying it to a new variable. That is, x satisfies Y as Y
will only compile if x
is assignable to Y
(the satisfies
check) and the whole expression will be treated as type Y
(the as
assertion):
to provide context that param
should be
const fn = <T extends AB>(param: T): T => { // okay
switch (param satisfies AB as AB) {
case 'a':
return 'a' as T;
case 'b':
return 'b' as T;
}
};
These approaches, of course, lose the generic behavior, so neither "a"
nor "b"
will be seen as assignable to the generic type parameter T
. That means you still need to use the type assertions in return "a" as T
and return "b" as T
. There are often ways to refactor code to maintain the generic behavior, but they will necessarily not use a switch
/case
statement and are therefore out of scope for the question as asked.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.