Typescript Type Guard wont affect the variable type if set indirectly

Issue

TL;DR Difference between link1 (Working) vs link2 (not working)

TypeGuard

function hasAllProperties<T>(obj: any, props: (keyof T)[]): obj is T {
    return props.every((prop) => obj.hasOwnProperty(prop))
}

Assume a variable msg that can be of 2 types – kind1 or kind2.

interface kind1 {
    propertyA: number
}

interface kind2 {
    propertyB: number
}

let msg: any = {
    propertyA: 1
}

In link1 the type guard is accessed directly and is working expected.
In link2 a boolean variable isKind1 is initially set to false and only set to true if the type guard check is passed.

let isKind1 = false
let isKind2 = false

// Check msg properties
if (
    hasAllProperties<kind1>(msg, [
        "propertyA",
    ])
) {
    console.log("Kind1")
    isKind1 = true
} else if (
    hasAllProperties<kind2>(msg, [
        "propertyB",
    ])
) {
    console.log("Kind2")
    isKind2=true
} else {
    throw `invalid ❌`
}
console.log("Check passed ✅")

Thus in link2 , I cannot think of a condition where if isKind1 === true but msg is not of type kind1

if (isKind1) {
    // Why is msg not of only type kind1
    console.log(msg.propertyA)
}

Solution

This is one of many situations where your code is correct, but Typescript just doesn’t have a rule that would allow the compiler to prove it. The compiler cannot generally prove every provable property of your code.

To explain this case more specifically, your first example works in Typescript 4.4+ because of a new feature, referred to as Control Flow Analysis of Aliased Conditions and Discriminants in the change notes. Essentially, the compiler has a specific rule which applies in your first example because in every execution path, the type guard’s result is assigned directly to the variable isKind1; but the rule does not apply in your second example, because the value assigned to isKind1 is the literal value true, which is not a type guard.

Answered By – kaya3

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published