deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

TypeScript 6.0's strictBuiltinIteratorReturn replaces any with undefined for iterator returns

TypeScript 6.0 adds --strictBuiltinIteratorReturn, which types built-in iterator return values as undefined instead of any, flagging code that passed arguments the runtime silently ignored.

TypeScript 6.0's strictBuiltinIteratorReturn replaces any with undefined for iterator returns

TypeScript 6.0 introduces a new compiler flag, --strictBuiltinIteratorReturn, that changes how built-in iterators are typed. According to a dev.to article covering the release, the flag sets the relevant return type parameter to undefined instead of any, which means the compiler now rejects code that passes values to .return() or .throw() on iterators that discard those values at runtime.

A design decision dating back to TypeScript 2.3

The dev.to piece traces the problem to a choice made in TypeScript 2.3, when the iterator return type for built-in objects was set to any. At the time, in 2017, JavaScript iterators in the wild handled return values inconsistently, and typing everything as any was a pragmatic way to support them without breaking existing code. The cost was a hole in the type system: TypeScript accepted arbitrary arguments passed to iterator protocol methods even when the underlying iterator ignored them.

This matters because array, string, and set iterators treat the argument to .return() as a no-op — behavior the JavaScript specification defines explicitly. Under the old typing, runtime behavior and type signatures diverged, and those mismatches typically only surfaced during refactoring or while debugging unexpected iterator state.

What changes when the flag is on

With --strictBuiltinIteratorReturn enabled, the TReturn parameter in Iterator<T, TReturn, TNext> is inferred as undefined for built-in iterators such as arrays, maps, and strings. That inference cascades through every function that accepts or returns an iterator. The article identifies three places where the change becomes visible: explicit iterator method calls, for-of loops that interact with iterator state, and generator functions that yield values.

Code that manually drives an iterator is the most likely to break. In the article's example, calling iter.return({ cleanup: true }) on an array iterator now triggers the error "Argument of type '{ cleanup: boolean }' is not assignable to parameter of type 'undefined'." As the article frames it, such code was incorrect all along — the runtime dropped the argument — yet TypeScript 5.x compiled it without complaint. The new errors are not false positives; they mark spots where code assumed an iterator would consume a value it never processed.

Where real code breaks

Two patterns are called out as the most common sources of new type errors.

The first is middleware that wraps iterators for logging, rate limiting, or transformation. These wrappers often forward metadata to the source iterator's .return() on the assumption that something downstream might use it. Built-in iterators ignore such arguments, so the compiler now rejects them unless the wrapper explicitly types its source as an iterator that accepts return values.

The second is generator functions with typed return values. Before 6.0, a generator could return a type that did not match its declared iterator return signature, such as returning a number from a generator declared as Generator<number, string, void>. The new rules require exact alignment between the two.

Fixes depend on intent. If the goal was to signal data to a custom iterator, the function signature should declare an iterator type whose return parameter accepts that data. If the goal was simply to close the iterator, dropping the argument works, since .return() can be called without parameters on built-in iterators.

Migration and rollout

Notably, the flag is not folded into --strict in TypeScript 6.0. According to the article, this is deliberate: it gives teams time to migrate before a future major version enforces the stricter behavior. Migration involves auditing iterator-consuming code for manual .next() calls with arguments, explicit .return() invocations, and generator functions that yield or return typed values. An opt-out also remains available for codebases that depend on legacy iterator libraries expecting the old any behavior.

Why it matters

The errors this flag produces point at genuine bugs: places where code believed an iterator would consume a value that the runtime silently discarded. For teams with iterator-heavy codebases, enabling it early in TypeScript 6.0 offers a controlled way to find those spots before stricter enforcement arrives in a later release. The change also fits a broader pattern in TypeScript's evolution — steadily closing long-standing any-shaped gaps so that static types reflect what JavaScript actually does. Turning the flag on now is a cheap audit; waiting until it becomes mandatory turns the same work into an abrupt, deadline-driven breakage.

  • #typescript
  • #javascript
  • #static-typing
  • #iterators
  • #compilers