michaeljsalo
michaeljsalo

Reputation: 642

Why does an array 'extend' a readonly array in TypeScript?

TypeScript supports conditional types, using 'extends' keyword and ternary operators.

Example conditional types that eval to true:

type C1 = 7 extends number ? true : false
type C2 = 7 extends number | string ? true : false
type C3 = 7 extends 7 | 'cat' | null ? true : false
type C4 = [1,2,3] extends [1,2,3] ? true : false
type C5 = any[] extends any[] ? true : false

I understand the conditions above (even if 'extends' seems odd in a non-OOP context).

The following conditions I don't understand:

type Q1 = any[] extends readonly any[] ? true : false  // evals to true
type Q2 = readonly any[] extends any[] ? true : false  // evals to false

Why does Q1 evaluate to true, and Q2 evaluate to false? I expected the opposite, as the readonly array seems 'more specific' than the general array.

What does 'extends' really mean in this context?

I am using TypeScript Playground to test these conditions.

Upvotes: 4

Views: 1374

Answers (2)

Adam
Adam

Reputation: 2897

It's definitely confusing.

Expected behavior

type A = 'readable'|'mutable' extends 'readable' ? true : false; // false
type B = 'readable' extends 'readable'|'mutable' ? true : false; // true

The way I made any sense was to walk through assigning things.

const C = [1]; // inferred as Array<number>
const D:ReadonlyArray<number> = C; // declare that the mutable array is readonly
const E:Array<number> = D; // error when assigning a ReadonlyArray to Array.  

The error seems arbitrary. It could have been that C wasn't assignable to D.

My guess is that they went with it because of historical code in the parser. When parsing a file with something like const F:ReadonlyArray<number> = [1], [1] is probably first inferred as Array. They needed a way to declare an already-inferred mutable array as readonly. Hence, Array is assignable to ReadonlyArray.

That sort of makes sense, though feels like a shortcut to get around rewriting the parser.

Upvotes: 0

Silvio Mayolo
Silvio Mayolo

Reputation: 70307

extends means "is a subtype of". Nothing more and nothing less. And "A is a subtype of B" means "every instance of A is assignable to a variable of type B". Again, nothing more and nothing less.

So the only question we need to ask is: is a T[] assignable to readonly T[]? Well, readonly T[] allows us to access the elements of the array. T[] allows us to access the elements of an array and also to mutate it. So T[] supports all of the operations of readonly T[] and then some. Hence, the relationship holds.

Upvotes: 6

Related Questions