Consistent intersection type with array and tupleΒ #50346
Description
Suggestion
π Search Terms
array, tuple, intersection, covariance, #32926, #33760
β Viability Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
β Suggestion
I think this is rather theoretical approach for the behavior of intersection types and covariance for the consistency.
If this doesn't meet the design policy, feel free to close this.
π Motivating Example
Comprehension check for the intersection type and covariance
With my understanding of TypeScript and set theory, intersection type seems to obey following:
A & B β A
A & B β B
Also covariance/contravariance can alter the direction of such relation.
A β B
If F needs to be covariant:
F<A> β F<B>
then F<A> & F<B> is equivalent to F<A>
If F needs to be contravariant:
F<A> β F<B>
then F<A> & F<B> is equivalent to F<B>
Current behavior of finite/infinite computed property
Now consider the following object types
type Small = {
[k in 'a' | 'b']: number
}
type Big = {
[k in 'a' | 'b' | 'c']: number
}
let small: Small = { a: 1, b: 1 }
let big: Big = { a: 1, b: 1, c: 1 }
// small = big // (OK)
// big = small // (Invalid)
This makes sense. Note that this somehow behaves contravariant, while with the following behaves covariant.
type Big = {
[k in string]: number
}
Here is the #playground for a glimpse.
What should be T[] & [T]
?
It's basically nonsensical to intersect a tuple type with a variable-length array type;
I already read this from #33760 (comment) . But with the theoretical point of view, I just wonder whether this is helpful for the consistent implementation for future or not.
I don't know the implementation detail of this, but along the empirical tests, I think [T]
is considered like:
[T] = {
length: 1
"0": T
// and other array's properties
}
And T[]
resembles:
T[] = {
length: number
[index in number]: T
// and other array's properties
}
Currently, with TypeScript v4.7.4, the result is a little bit weird. #playground
type T = number[] & [number]
let t: T[1] // ??? no error
let len: T["length"] // 1
Is this because 0
is not computed property? How about [T, T]
or [T, T, T]
?
π» Use Cases
Literal type 0
should be considered as finite subset of number
, and by "Current behavior of finite/infinite computed property", the computed property of index, should be narrowed into 0
. Note that length
is already narrowed into 1
.
// suggested behavior
type T = number[] & [number]
let t: T[1] // should be error or never
let len: T["length"] // 1
I understand that this might impractical, and nobody do number[] & [number]
. But I'm not sure that, there is no guarantee for other complex composition never make such result. I hope this insight would be helpful for TypeScript team.