[compiler] Closures capture let loop variables by final value, not per iteration
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.3k
- Forks
- 641
- Avg merge
- 12h 4m
- Merged PRs (30d)
- 57
Description
Closures capture let loop variables by final value, not per iteration. Standard TypeScript gives each for (let i ...) iteration a fresh binding; static TypeScript shares one binding, so every closure sees the loop's final value.
Repro: https://makecode.com/_1m7JkiYRK5Yw
const fns: (() => number)[] = []
for (let i = 0; i < 4; i++) {
fns.push(() => i)
}
console.log(fns.map(f => f()).join(","))
// standard TypeScript: 0,1,2,3
// static TypeScript: 4,4,4,4
Confirmed on the simulator backend and on a micro:bit V2; both backends agree, so this is emitter-level scoping, not a backend bug. Workaround: capture through a function parameter (function mk(v: number) { return () => v }), which binds per call.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the linked MakeCode repro and compare the closure results with standard TypeScript. Trace the emitter-level scoping for for (let i ...) loops; done means each generated closure observes its iteration's value, with the simulator and micro:bit V2 behavior agreeing.
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
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 64/100