I have some TypeScript code that I mean not to be executed, but it does, why?
let a:[boolean, ... readonly string[]] = [true, '1', '2', '3'];
a[0] = false;
a[1] = 'one'; //no error, but it should be error
CodePudding user response:
The reason your code doesn't produce an error is that the type of a
is immediately reduced by the compiler to the fully-mutable type [boolean, ...string[]]
, despite your annotation:
let a: [boolean, ... readonly string[]] = [true, '1', '2', '3'];
type A = typeof a;
// type A = [boolean, ...string[]]
And therefore none of the elements of a
are read-only. So that's the answer to the question as asked.
The obvious follow-up question is "why does it get reduced to a fully mutable type?". Unfortunately I can't find any authoritative source for this, so anything I could say here would be speculation. Here it comes:
The type [T, ...A]
is the type you get when you spread an array of type A
into the end of an array literal that starts with a value of type t
. The fact that the following function compiles with no error is evidence of this:
function f<T, A extends readonly any[]>(t: T, a: A): [T, ...A] {
return [t, ...a]
}
And if I have a read-only string array and spread it to the end of an array literal, the resulting array will not be read-only:
const strs: readonly string[] = ["x", "y"]
const result = f(true, strs);
// const result: [boolean, ...string[]]
So that's my guess. If anyone finds any canonical source (e.g., TS doc or GitHub issue) that confirms or refutes this, I'd be interested to know about it.
CodePudding user response:
Spreading an array/tuple just orderly lists out the element of that array.
It just plucks out the values from the array without actually modifying/transferring the inferred characteristics
let xs = [1,2,3,4]
let ys = [...xs] // [1,2,3,4]
Hence when we do
let a:[boolean, ... readonly string[]] = [true, '1', '2', '3'];
We are just saying pluck all the values from the readonly string array
and not that make the values as readonly
.
Thus the behavior.
CodePudding user response:
It works because readonly
is only the individual elements themselves. The array itself is still mutable.
If you did
let a:readonly [boolean, ... readonly string[]] = [true, '1', '2', '3'];
a[0] = false; // error
a[1] = 'one'; //error
then, yes, it errors because you've marked the array itself as readonly, not just some of its values.