Skip to content

TS2454 false positive when a callback mutates an outer let and returns the enclosing function's parameter #63981

Description

@bent0b0x

🔎 Search Terms

TS2454, used before being assigned, definite assignment, definite assignment analysis,
control flow analysis, closure, captured variable, outer variable, compound assignment,
strictNullChecks, false positive, tsgo, TypeScript 7

🕗 Version & Regression Information

  • Fails: typescript@7.0.2 (stable), typescript@7.1.0-dev.20260822.1, @typescript/native-preview@7.0.0-dev.20260421.2, @typescript/native-preview@7.0.0-dev.20260707.2
  • Works: typescript@6.0.3 and typescript@5.7.3

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=6.0.3&strict=true&target=99&module=99&moduleResolutio

💻 Code

export {};

let count: number;

function makeCallback(p: string) {
  return () => {
    count += 1;
    return p;
  };
}

count = 0;
makeCallback('x')();

Compiled with --strict --noEmit --target esnext --module esnext --moduleResolution bundler

🙁 Actual behavior

TypeScript 7 reports:

minimal2.ts(7,5): error TS2454: Variable 'count' is used before being assigned.

🙂 Expected behavior

No error, as in 5.7 and 6.0. count is declared outside the arrow function, so the reference inside the closure is an "outer variable" reference and definite-assignment analysis should be suppressed for it. count is also definitely assigned at module top level before the callback can run.

Additional information about the issue

Each of these single changes, in isolation, makes the error disappear:

Variation TS 7
return 'x' instead of return p ✅ clean
count = 1 instead of count += 1 ✅ clean
count = 0 moved above makeCallback ✅ clean
removing export {} (script rather than module) ✅ clean

So it seems to need all of: a module; a compound assignment to an uninitialized outer let
inside a closure; that closure returning a parameter of the enclosing function declaration;
and the outer assignment appearing after that declaration.

Found while migrating a real codebase from 5.7 to 7. It's a common test shape — a counter
incremented inside a mock callback that returns a canned response, reset in beforeEach — and
produced six errors in one test file that 5.7 and 6.0 both accept.

Metadata

Metadata

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions