Why doesn't typescript narrow types with control flow and never when using arrow functions
Asked Answered
C

1

5

Typescript refuses to narrow with a call to fail but will narrow with a call to fail2. Is this a bug in typescript?

const fail = (message?: string): never => {
    throw new Error(message);
};

function fail2(message?: string): never {
    throw new Error(message);
}

const getData = (): string | null => {
    return "the-data";
}



export const loadDataOrError = (): string => {
    const data = getData();

    if (data === null) {
        // Swap the below and see that it works
         
        // fail2();
        fail();
    }

    // This errors
    return data;
};

here is a playground if you want to try switching the comments and seeing the error vanish.

Screenshots for clarity
With an error

With an error

Without an error

Without an error

Coverage answered 26/2, 2023 at 17:32 Comment(4)
Even if fail can be reassigned, the new value will still return never so that's not a good reason. In your example you are using as which is unsound by design, so obviously you can get weird behavior.Syringomyelia
Agreed @GuillaumeBrunerie Also let us not forget that it is a const so the whole argument is unsoundCoverage
I think you should file an issue. This is not normal.Adamis
Just found this issue. I guess I'll answer my own questionCoverage
C
6

According to this open GitHub issue this is a limitation of how type narrowing currently works in typescript. If you want to remedy it you can explicitly annotate arrow function types like so:

const fail: (message?: string) => never = (message) => {
    throw new Error(message)
}
Coverage answered 26/2, 2023 at 20:55 Comment(1)
Yeah, it's all about explicit type annotations on the thing you're calling. It's a fairly annoying limitation with assertion functions.Undulate

© 2022 - 2024 — McMap. All rights reserved.