Type guard with other variable

Viewed 473

is it possible for an other variable to function as type guard?

assume some code like this

let foo: string | null = Math.random() > .5 ? null : 'bar'
const otherProp = true
const test = foo !== null && otherProp
function foobar(x: string){}

if i would now call foobar(foo) i assume a warning as foo can be null or string and only string is allowed

but if i call it like

if (test){
    foobar(foo)
}

it should not warn me as test is only true if foo is not null and so only string remains as type

is something like this possible?

Playground

3 Answers

This is not possible in TS right now. because it adds considerable complexity to their Control-Flow Analyzer. other than explicitly putting foo !== null as the if expression, there two possible things to do:

1- using Assertions:

if (test){
    // since test is equal to (foo !== null)
    foobar(foo as string)
}

But it's a bit bug-prone because you may change the test variable in the future then that type assertion would lead to bugs because foo is no longer for sure a string.

2- User-Defined Type Guards

The better way in my opinion is using user-defined type guard as @VLAZ correctly mentioned.

   const isNotNull = <T>(x:T): x is Exclude(T, null) => x !== null
    
   if (isNotNull(foo)){
     foobar(foo)
   }

This is currently not possible (see this issue).

Here's a workaround. It assigns wrappedFoo a type which either contains a string and true, or null and false. After checking for the boolean value, Typescript knows which of those two it is:

const foo: string | null = Math.random() > .5 ? null : 'bar'
const wrappedFoo = foo === null ? { foo, isString: false as const } : { foo, isString: true as const }

if (wrappedFoo.isString){
    console.log(wrappedFoo.foo.length)
} else {
    console.log(wrappedFoo.foo.length) // Error Object is possibly 'null'
}

Next version of Typescript (4.4) will support control flow analysis of conditional expressions. The only change you'll need to do is let foo -> const foo, because

Narrowing through indirect references occurs only when the conditional expression or discriminant property access is declared in a const variable declaration with no type annotation, and the reference being narrowed is a const variable, a readonly property, or a parameter for which there are no assignments in the function body

Playground v4.4

Related