Home > other >  Type guard removing string literal types
Type guard removing string literal types

Time:08-10

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:

Playground link to code using TS version 4.8.0-dev.20220809

  • Related