• Sound mode S-09, pre-alpha
  • Design record 0007, Accepted

TSR0901

'{0}' may have changed since it was narrowed: '{1}' on line {2} can modify it. Copy it to a 'const' before that point.

A property was narrowed, then a call, await or assignment could have changed it before it was used. Copy it to a const before narrowing.

Rule S-09: Narrowings are dropped when they could be invalidated (level: sound). Reported by ts-r-check and ts-r-lsp in files that opt in to sound mode. See Sound mode.

What it means

A narrowing of a property reference (o.p, this.p) is dropped at the first point that could change it: a call or new (except a curated list of lib functions that can't), await, yield, or an assignment to any property. Only properties declared readonly all the way down keep their narrowing. It's reported only where the code relies on the dropped narrowing.

Example

Spec case from design record 0007 (checked by the record's case harness, not by this site's build)
// The caller runs between yields and can change o.v.
function* lengths(o: { v: string | null }): Generator<number, void, unknown> {
  if (o.v !== null) {
    yield 0;
    yield o.v.length; // @expect TSR0901
  }
}
export const first: IteratorResult<number, void> = lengths({ v: "a" }).next();

How to fix

  • Copy the property to a const and narrow the copy: const s = conn.socket; if (s !== null) { await flush(); s.send(m); }.
  • Declare the property readonly if it never changes.
  • The planned quick fix introduces the const copy and rewrites the uses.

Sound mode is defined in design record 0007 (accepted 2026-10-15).