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

TSR0501

'{0}' is not assignable to '{1}': mutable '{2}' is invariant in sound mode, so writes through '{1}' could break '{0}'. Use '{3}' if it is only read.

A mutable array, tuple, Map or Set is used as one with a wider element type, so writes could break it. Use the readonly type if it is only read.

Rule S-05: Mutable built-in containers are invariant (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

In sound files, T[], Array<T>, mutable tuples, Map, Set, WeakMap and WeakSet are invariant: Dog[] is not assignable to Animal[], because code holding the Animal[] could push a Cat into it. The readonly forms (readonly T[], ReadonlyMap, ReadonlySet, Iterable<T>) stay covariant. It's reported where tsc reports assignability errors; {3} names the readonly form.

Example

Spec case from design record 0007 (checked by the record's case harness, not by this site's build)
// string[] to (string | number)[]: a number could be pushed into the string array.
const strings: string[] = ["a"];
const mixed: (string | number)[] = strings; // @expect TSR0501
export const n: number = mixed.length;

How to fix

  • If the target is only read, make it readonly: readonly Animal[], ReadonlyMap<string, Animal>.
  • Make function parameters that never write readonly (the planned quick fix does this).
  • Copy when ownership moves: [...dogs], new Map(dogMap).

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