- 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
// 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
constand narrow the copy:const s = conn.socket; if (s !== null) { await flush(); s.send(m); }. - Declare the property
readonlyif it never changes. - The planned quick fix introduces the
constcopy and rewrites the uses.
Sound mode is defined in design record 0007 (accepted 2026-10-15).