When creating a TS type guard, it seems like string literals are removed from the narrowed type once you add undefined
or null
to the predicate type. Is there any way to use a type guard that has a predicate like x is 'my-string' | undefined
?
Or, in other words: Assume we have a type guard with the predicate x is 'my-string'
. Whenever checking a variable using this guard, TS will correctly narrow the passed variable down to be the literal type 'my-string'
. However, once you change the predicate to x is 'my-string' | undefined
, TS will narrow the type of a checked variable to undefined
. I expected it to be 'my-string' | undefined
. Why is that? Are type guards not meant to check for string literals?
Example: Open in TS Playground
/*
When using "typeGuard1", you will notice that the guarded type, for some reason, gets narrowed down to `undefined`.
It works fine with "typeGuard2".
*/
function typeGuard1(x: any): x is 'some-literal-string-type' | undefined {
return true;
}
function typeGuard2(x: any): x is string | undefined {
return true;
}
// The following setup is used to make sure the compiler does not magically infer anything.
const foo = getFoo();
function getFoo(): string | undefined {
if (Math.random() > 0.5) {
return 'This is foo'
}
return undefined;
}
if (typeGuard1(foo)) {
// For some reason, `foo` gets narrowed down to `undefined`. This does not happen if you use `isFoo2(foo)`, or if you remove "| undefined" from "typeGuard1".
console.log(foo?.length);
}
if (typeGuard2(foo)) {
console.log(foo?.length);
}
CodePudding user response:
This is a known bug, see microsoft/TypeScript#31156. It has been fixed in microsoft/TypeScript#49625 which should be released with TypeScript 4.8. Once this happens your code will work as intended: