microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.34k stars 12.4k forks source link

Calling never-returning method on a subtype of function does not consider following code as unreachable #60059

Closed AlCalzone closed 2 hours ago

AlCalzone commented 2 hours ago

🔎 Search Terms

function member never ts2534

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.0-dev.20240925#code/C4TwDgpgBAaghgGwJYBM7CQewHYDECu2AxhjlALxQAUAdHXAM4MQBOp2DAXFIQNbaYA7tgDaAXQCUFAHxQAbplQBuAFAqk2YKwBmcItHjI0WqBAAeW7CgaxEqdFjyESjqAG8VASF1IEVCdzYEHKsqgC+atrO7PJ2xhAAkpqs2Ii09Eys7Fw82PxCopLcCqjuXkja1ACEjMxsjgw0wawgVABCmJgIEHDYElIenp7AABYsQlBBglAAoizjLFQARHJxDmQ+3ShLEqqeEREqRDgMwLFG6NCUqxdaSVosqQg0AEYaKFSEKBDaGhAoUkYtluEFUN3sWhomwo1Ck5Fk4PiVF0CGYgJsfAEwigQKCIRYqki0Vcm38gWaLDKUHOEIgULgvn8qmpAHoWdSORyAHoAfhUhyixBiQVO-1wDL8AUmFLKnjZk0wpnmmEpI1YEC8pN2XnlvP5ahFWhQ4sZuyg8tGSBso3Gghs7wg-xUCEwAHNlihSgIzjahDtVEA

💻 Code

type ValidationFunction = (...assertions: unknown[]) => void;

interface Validate extends ValidationFunction {
    fail(): never;
}

function validateInternal(...assertions: unknown[]): void {
    if (!assertions.every(Boolean)) {
        throw new Error("validation failed");
    }
}

const validate = validateInternal.bind(undefined) as Validate;
validate.fail = () => validate(false) as unknown as never;

function fail(): never {
  validate.fail();
  //        ^? (method) Validate.fail(): never
}

function nestedFail(): never {
    // no error here
    fail();
    // ^? function fail(): never
}

nestedFail(); // this throws indeed
log("did not throw");

🙁 Actual behavior

TS complains about the never return type of function fail():

A function returning 'never' cannot have a reachable end point.(2534)

🙂 Expected behavior

No errors

Additional information about the issue

No response

MartinJohns commented 2 hours ago

This is working as intended / a design limitation. See #32695.

A function call is analyzed as an assertion call or never-returning call when

  • the call occurs as a top-level expression statement, and
  • the call specifies a single identifier or a dotted sequence of identifiers for the function name, and
  • each identifier in the function name references an entity with an explicit type, and
  • the function name resolves to a function type with an asserts return type or an explicit never return type annotation.

validate does not have an explicit type.

AlCalzone commented 2 hours ago

Edit: I was too fast. Thanks for the update, it does work with an explicit type annotation on validate.

MartinJohns commented 2 hours ago

Is that because validate has no type annotation?

Correct, it has no explicit type. I just edited my comment to add that information. This limitation was added for performance reasons.