Variable isn't narrowed within a capturing closure
See original GitHub issueTypeScript 3.7.2 Playground link
Compiler Options:
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"useDefineForClassFields": false,
"alwaysStrict": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"downlevelIteration": false,
"noEmitHelpers": false,
"noLib": false,
"noStrictGenericChecks": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"esModuleInterop": true,
"preserveConstEnums": false,
"removeComments": false,
"skipLibCheck": false,
"checkJs": false,
"allowJs": false,
"experimentalDecorators": false,
"emitDecoratorMetadata": false,
"target": "ES2017",
"module": "ESNext"
}
}
Input:
let i: number | undefined;
i = 0;
let j:number = i+1; // works
(k: number) => k === i+1; // error: Object i is possibly undefined
Output:
"use strict";
let i;
i = 0;
let j = i + 1; // works
(k) => k === i + 1; // error: Object i is possibly undefined
Expected behavior:
The compiler should not complain about the last i+1
because it’s clearly a number
type after assigning 0
to it.
I suspect the closure uses the type of i
from the let
statement and ignores it being narrowed down later on. Is this expected behavior?
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:12 (4 by maintainers)
Top Results From Across the Web
Documentation - Narrowing - TypeScript
Argument of type 'string | number' is not assignable to parameter of type ... and TypeScript can understand it to narrow types in...
Read more >Closures: Anonymous Functions that Can Capture Their ...
Closures are usually short and relevant only within a narrow context rather than in any arbitrary scenario. Within these limited contexts, the compiler...
Read more >Why and when an unexecuted closure can capture outer ...
Compile time is not necessarily the same as definition time. The closure captures variables when it is created, not when its body is...
Read more >Closures: Anonymous Functions that Can Capture Their ...
Rust's closures are anonymous functions you can save in a variable or pass as arguments to other functions. You can create the closure...
Read more >A lambda is not necessarily a closure | Hacker News
A more aggressive method would be to discover that all references to f are within (fun q -> ...) and inline the free...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
If I had to draw the most realistic picture of a warning on a closed-over variable that really shouldn’t exist, it’d look like this:
It seems like the rule is “If a closure is lexically after all assignments to a bare identifier, then the last narrowing can apply”.
You can break this, though:
So the rule would need to be amended to “All assignments to a bare identifier occur lexically and CFA-before the closure expression”, though possibly that could be broken too.
Duplicate of #9998.
Typescript warns you that the
i
can be undefined when the function is called:Playground.