Sorting a read-only array is OK if it has only 1 or 0 items

Published on in JavaScript and TypeScript

Huh. But if the array has 2+ items, attempting to sort it will cause a TypeError.

Table of contents

Example 1

Object.freeze([]).sort() // OK
Object.freeze(['a']).sort() // OK
Object.freeze(['a', 'b']).sort() // TypeError

Errors in different browsers:

  • Chrome / Edge: "TypeError: Cannot assign to read only property '0' of object '[object Array]'"
  • Firefox: "TypeError: 0 is read-only"
  • Safari: "TypeError: Attempted to assign to readonly property."

Example 2

const arr = []
arr[1] = 'b'
Object.freeze(arr)
arr.sort() // TypeError

The errors are slightly different:

  • Chrome / Edge: "TypeError: Cannot add property 0, object is not extensible"
  • Firefox: "TypeError: can't define property 0: Array is not extensible"
  • Safari: same as in Example 1

Pondering

I use mainly Firefox. Its error message "0 is read-only" is not very enlightening, so when investigating a related bug, I didn't grasp at first that the problem was about a read-only array. Meh.

If an array is empty or has only one item, sorting the array won't modify the array because there are not enough items to move around. So no error even if the array is read-only. Makes sense but was surprising (hence this blog post).

On the other hand: if a read-only array has 2+ items and is already sorted, trying to sort it causes a TypeError, even though the array is already sorted! Makes sense but is also mildly confusing – why is the array being modified if it's already sorted?

If a read-only array has only empty slots, sorting it is OK, which is weird:

const arr = Object.freeze(Array(3))
arr.length //=> 3
arr.sort() // OK but weird

Fix 1

The sort() method sorts an array in-place, i.e. sorting an array modifies it.

You might want to first copy the array with slice() and then sort the copied array:

const arr = Object.freeze(['a', 'b'])
arr.sort() // TypeError
arr.slice().sort() // OK

Fix 2

TypeScript would have spotted the error for me right away:

const arr = Object.freeze(['a', 'b'])
arr.sort()
//  ^? Property 'sort' does not exist on type 'readonly string[]'.

On the other hand, the TypeScript compiler doesn't seem to know that sorting a read-only array is OK if the array has only 1 or 0 items:

const arr = Object.freeze(['a'])
arr.sort()
//  ^? Property 'sort' does not exist on type 'readonly string[]'.

// 👆 Compile-time error but no runtime error, so 🤷‍♂️

Ha! 😄

On a serious note: I think it's better that the TypeScript compiler always disallows sorting read-only arrays.