Type refinement "forgetting" the value within a statement?

138 Views Asked by At

Moving from flow I am often refining mixed types to some better format. In typescript I use unknown instead of mixed. Resulting in the following codepiece for error handling:

type err = {
    cause: unknown,
    msg: string,
}

if (typeof err.cause === 'object' && 
    err.cause !== null && 
    'incomplete' in err.cause && 
    Array.isArray(err.cause.incomplete)) {
    something(err.cause.incomplete[0]);
    //..something
}

As far as I can tell this should work? - I'm first checking if the cause is a valid object, and then test if incomplete is inside it.

However an error shows at the Array.isArray:

Error:(26, 37) TS2339: Property 'incomplete' does not exist on type 'object'.

So what's making typescript "forget" the previous refinement, and how to make it work?

1

There are 1 best solutions below

0
joshwilsonvu On

It does refine unknown to object, but you can't access arbitrary fields on an object, and 'incomplete' in err.cause doesn't actually refine the type. To do that, you'll need a type guard. Example:

function f(o: object) {
    if ("incomplete" in o) {
        console.log(o.incomplete); // error
    }
    if (hasIncomplete(o)) {
        console.log(o.incomplete); // okay
    }
}

function hasIncomplete(o: object): o is HasIncomplete {
    return o.hasOwnProperty("incomplete");
}

interface HasIncomplete {
    incomplete: any;
}