Yes, this is a real problem and I've been bitten by it more times than I can count. Now I always keep this handy website ready: https://doesitmutate.xyz/
const a = [1,2,3]
a.push(4)
const b: readonly number[] = [1,2,3]
b.push(4) // Property 'push' does not exist on type 'readonly number[]'. > Object.freeze([1,2,3]).push(4)
TypeError: can't define array index property past the end of an array with non-writable length (firefox)
Uncaught TypeError: Cannot add property 3, object is not extensible (chrome)
Of course, it will only blow up at runtime. But better than not blowing up at all, creating heisenbugs and such.I often find myself writing classes where the last step of a constructor is to Object.freeze (or at least Object.seal) itself.
I think this is the kind of thing you just have to learn when you use any language. But when you're switching between half a dozen, being able to rely on consistent founding design principles really makes things easier. And when there aren't any, this kind of guide helps.
The only real downside is the lack of return values mean you can't chain mutations, but personally that never bothered me.
Mutates: push, pop, shift, unshift, splice, reverse, sort, copyWithin
Does Not Mutate: everything else