-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Detection of unreachable code after call to function returning never
is broken for functions exported via intermediate variable
#60368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
never
is broken for functions exported via intermediate variablenever
is broken for functions exported via intermediate variable
This is working as intended, see #32695:
|
@MartinJohns nice find 👏, thanks! Indeed, this condition isn't respected in my case:
In this case, from the perspective of an end-user, it feels like “room for improvement”: it’s surprising that this feature sometimes works, but sometimes doesn’t. → With that, I’ll leave the issue open, up to TS triagers to close as Won’t Fix, or label/prioritize. Oh, one last thing to Martin or a passerby or someone maybe fixing this: why the hell am I doing exports via an intermediate const? Because I need to mock them for unit tests, but ESM exports are immutable and thus cannot be stubbed/mocked. Said differently, I’m applying recipe |
Your issue is this:
Both
Really no big deal, this issue has come up so often already. Yours is just one of many. |
@MartinJohns Iiiiiii... don’t understand.
What am I missing? |
const {fnNeverWithConstExport} = libConst; @ronjouch The identifier that's actually called is the one that needs to be typed before the inference pass runs, eg: https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEA3W8IEsBGBhA9gOwGcAXALngG8AoeG+AMzwDkREQYB1NIgC10KICiADwAOOGKXgAKAJTwAvAD54eFm0oBfSpTD5i9Jmo5deewaPGTZC5atYwFqTH2IA6Bs3uceL82IkA3NoeRt6m-ML+RLJBuoQ4ECCuEDgA5lIA5ADuOFkZMkFAA |
@nmain sorry, still puzzled:
import {libConst} from './lib-export-const.mjs'
const /** @type {{ fnNeverWithConstExport: () => never }} */ {fnNeverWithConstExport} = libConst; // NEW ANNOTATION
import {fnNeverWithEsmExport} from './lib-export-proper.mjs'
/** @returns {never} */
function fnSameModule() {
process.exit(0);
}
function withSameModule() {
fnSameModule();
console.log('GOOD: properly seen as unreachable');
}
function withExport() {
fnNeverWithEsmExport();
console.log('GOOD: properly seen as unreachable');
}
function withConst() {
fnNeverWithConstExport();
console.log('BAD: incorrectly missed as unreachable');
}
// console.logs nothing (runtime behavior all good)
withSameModule();
withExport();
withConst(); |
Slightly different rule there; the details of #32695 explains it entirely; the "dotted sequence of identifiers" part is relevant.
Let's stick to the playground; there's nothing going on here that can't be represented there.
I believe that gets into #29526 (or at least, a JSDoc variant of it). Typing destructrings is weird, and because the whole object was typed, there's actually still an inference step to pull out the property. It works fine without destructuring: https://www.typescriptlang.org/play/?filetype=js#code/PQKhAIAECcFMBcCu0B2BncBvFsButoBfcEYAKADNEUBjeASwHsVwKUBlAQwFtYBZRgBNEAG1gAKAJRYy4cPAAW0RgHdwKUSIDcZQmTI1maeOBH0ARgGEjJgLxZWKAHJ4CAdXqLr6eAFEAHgAOjNDwAFyOXLwCwmLghDpkoBCQ8ACegbBYUuC2AHzqrkQk5IY+ji740B5eNgHBobmmFt7GAHRsle6eCq1+QSHwiVS0DMzgKj1R-EKiEtKYspE8M7HzOnJlaIxibSKMAObiAOQA4gDy5wAiEYHKmdAiaeBosLAsnBjUcJw0CpzmMTHSQ6PT6EZ0JgsSa1Hw5RZyTpFGq9OoDUJSDbgLY7WB7Q4nABCAEEbuB6LQQnA6E9wNx6GhXoJwJ9wN9YL9-oDYMDQWQgA |
@nmain indeed, now this makes sense and I'm a convinced capybara 👍👍👍. Thank you. To a potential TS dev looking into this: from a user perspective, this remains unpleasant to use: I wouldn't want to re-type all my imports where they are used! Hope it gets addressed someday. |
We're definitely aware that it would be nice if this worked, but the perf cost of inserting a control flow graph node at every function call was too high. |
This issue has been marked as "Design Limitation" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
🔎 Summary
Detection of unreachable after call to function returning
never
generally works.However, it appears broken when the
never
-returning function is exported via an intermediate variable.Here’s my best minimal demo:
(sorry, cannot do a Playground link, as my problem is inherently multi-file, and Playground only supports one file)
🕗 Version & Regression Information
⏯ Playground Link
Cannot share a Playground link, as Playground is single-file only, and my issue is about exports / multi-file
💻 Code
app.mjs
lib-export-proper.mjs
lib-export-const.mjs
🙁 Actual behavior
TS fails to understand the
never
-ness of thenever
-returning function exported via an intermediate variable.🙂 Expected behavior
I expect TS to understand that code following a function returning
never
will not run.This is useful e.g. for early exits useful to narrow a
string | undefined
into astring
.The text was updated successfully, but these errors were encountered: