Reachability analysis of `never` for `yield*` is unchecked
🔎 Search Terms
reachability never
🕗 Version & Regression Information
It's observed in all TS versions.
⏯ Playground Link
https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mAVAAjAUwG5oE4AoCUAXMgOJrrYCGUc2APOANZhwDuYANOltgHzIBvAFDJkUABbY2yAOTccMgNxCAvkKGhIsBCgC2lGGAKCRY7AE8To0eZhoANgBMU8vPmWiVyYIcr37lsLWyBAIAM5w9mgAdPZwAOa4Mj5gfgEy7qZqoqFgEVGxCUksUMjYaJQQ4pQARlEZympAA
💻 Code
function* never(): Generator<unknown,never> {
throw 'never';
}
function* main() {
try {
yield* never();
} finally {
console.log('finally');
}
console.log('not reachable');
}
🙁 Actual behavior
No error reported.
🙂 Expected behavior
There should be an error Unreachable code detected.(7027) at line 11 console.log('not reachable');.
Additional information about the issue
Reachability analysis is working for normal function: https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABGApgNxQJwBQEoBcy6WiA3gFCKJQAWmcA7ogOSoabMDc5AvueaEiwEiALYBDGGDxlK1TAE9ZVKmyx5uVHomBTxAG31KKKxBAQBnOPpQA6fXADm2ZrrAGjzXJsR8q5sCsbeycXMDgoREwUcQgacQAjGy9uPiA
It's broken for Promise and we have another issue tracking that one: https://github.com/microsoft/TypeScript/issues/34955
Generator functions are being used as coroutine in libraries like co.js, Effect.js, redux-saga, Effection, and more. It's common to write a coroutine operator that suspend the thread forever, thus taking function never(): Generator<Instruction, never> signature. It would benefit users if the TypeScript control analysis can infer unreachability after yield* never().
Related: https://github.com/microsoft/TypeScript/issues/56363
I think this comes down to the known limitation wherein never-returning functions can only affect control flow for a naked function call (which is the root cause of #34955 too); here, the call is only part of the statement because the return value is being passed to yield*. So it's similar to how
function never(): never { throw Error(); }
const x = never();
console.log("unreachable, not an error");
doesn't have an unreachable-code error either.
Run into this as well
function foo(): never {
throw new Error('foo');
}
(foo());
const x = foo();
console.log(foo());
foo();
// Only from this point forward it errors because of "unreachable"
console.log('hello');
I found this bug while I was looking into this bug which is somewhat related.