microsoft / microsoft/TypeScript
Narrowed `const` variables are widened in block-scoped closures
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- PR merge metrics
- PR metrics pending
Description
### 🔎 Search Terms
narrowing, cfa, closure,
### 🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about n/a
### ⏯ Playground Link
https://www.typescriptlang.org/play/?noUncheckedIndexedAccess=true&target=99&ts=5.8.0-dev.20250209#code/CYUwxgNghgTiAEYD2A7AzgF3mgXNjMAligOYDcAUBYQGbwAUa8AvK-AOQ1JLsCU8AbwrwRiVJmwBBCIShNm2SqPg0ArijAZCqeACMowevyHLlydFi5I8nbuxaLhogL5VlajVp0luh409MxCxVuGyt7BTRpWTQlFwpXChAADwAHJBgsAWcgA
### 💻 Code
```ts
declare const s: string;
if (s === 'foo') {
function bad() {
const foo: 'foo' = s;
}
}
// in non-strict code bad() might be accessible outside of the narrowed branch,
// so make sure we're strict
export {}
```
### 🙁 Actual behavior
error trying to use the variable as its narrowed value.
### 🙂 Expected behavior
No error. `bad()` is a local function only reachable within the scope in which `s` has been definitely narrowed to `'foo'`.
### Additional Information
Found organically in https://github.com/typescript-eslint/typescript-eslint/pull/10182#discussion_r1948337564 (with a narrowed member access corollary)
Contributor guide
Research direction
Start with the linked TypeScript Playground repro and the code sample showing a narrowed const inside a block-scoped function. Trace how control-flow narrowing is handled for local function declarations and verify that the example no longer reports an error while preserving the strict-mode behavior described in the issue.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100