Search code examples
typescriptreadonly

Difference between Readonly<[]> and ReadOnlyArray<>


Code 1:

let readonlyArray: ReadonlyArray<string | number> = ["test", 1, 1];

Code 2:

let readonlyArray: Readonly<[string, number]> = ["test", 1, 1];

Both lines of code seem to do similar things. I don't know what is the difference between declaring array as ReadOnly and ReadOnlyArray.

Also, which one is best for performance and why?


Solution

  • From a performance point of view, there should not be any difference between the two, as types are erased at runtime and the same javascript will run regardless

    There is a fundamental difference between the two types at compile time:

    Readonly<T> - Is a type that has the same shape as T but all the properties are read-only. In your case the T is the tuple type [string, number], so it will have all the properties of array as well as the indexes 0 and 1. So we can call the push method, but we can't reassign the concat method.

    let readonlyArray: Readonly<[string, number]> = ["test", 1, 1];
    readonlyArray.concat = ()=> {} // Not valid, concat is readonly 
    readonlyArray.push(1); // This is valid 
    readonlyArray[1] =  ""; // Invalid we cannot change a property by indexing 
    readonlyArray[3] =  ""; // Valid as it was not in the original tuple type 
    

    Edit: Since 3.4, typescript has changed the behavior of mapped types on array and tuples, so Readonly<[string, number]> is now equivalent to a readonly tuple readonly [string, number], so the errors are a bit different:

    let readonlyArray: Readonly<[string, number]> = ["test", 1];
    readonlyArray.concat = ()=> {} // Not valid, concat is readonly 
    readonlyArray.push(1); // This is not valid, no push method anymore
    readonlyArray[1] =  ""; // Invalid we cannot change a property by indexing 
    readonlyArray[3] =  ""; // Invalid now tuple length preserved
    

    </Edit>

    ReadonlyArray<T> is a true readonly array that does not have any methods that can change the array. In your case any item of the array can be either a string or a number:

    let readonlyArray2: ReadonlyArray<string | number> = ["test", 1, 1];
    readonlyArray2.concat = ()=> []; // Valid we can set the concat property on the object 
    readonlyArray2.push(1) // No push method, invalid
    readonlyArray2[1] =  ""; // Invalid it is read only 
    readonlyArray2[3] =  ""; // Invalid it is read only