Repository: TypeScript
Issue: Consistent intersection type with array and tuple
Labels: Discussion

# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## 🔍 Search Terms

<!--
  💡 Did you know? TypeScript has over 2,000 open suggestions!
  🔎 Please search thoroughly before logging new feature requests as most common ideas already have a proposal in progress.
  The "Common Feature Requests" section of the FAQ lists many popular requests: https://github.com/Microsoft/TypeScript/wiki/FAQ#common-feature-requests

  Replace the text below:
-->

array, tuple, intersection, covariance, #32926, #33760

## ✅ Viability Checklist

<!--
   Suggestions that don't meet all these criteria are very, very unlikely to be accepted.
   We always recommend reviewing the TypeScript design goals before investing time writing
   a proposal for ideas outside the scope of the project.
-->
My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).


## ⭐ Suggestion

<!-- A summary of what you'd like to see added or changed -->

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

<!--
  If you were announcing this feature in a blog post, what's a short explanation that shows
  a developer why this feature improves the language?
-->

### 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

```ts
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.

```ts
type Big = {
  [k in string]: number
}
```

Here is the [#playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBAygtgQwDZKgXigbwLACgpQDaA1lAJYB2UA5AtVAD40BG1AugFxQUCuczEAE54AvnjyhIUAEJkA5uix4CJclVr0m1VoxoBjdl178ho8bgD0FqJOiyFGHJesrSlKAGdggynM7c+AWFnKDFcPCQIYE9EFC54ZFRHKAQuAEYAGihmdNCIqOz5LntFTBT0rJyoTKg9XLC8KxjExWZ5AgImgHUyYAALKAAxSl6ILIB7YgA6Rus2h2aUDqhu3oGASQoAMxHgMahJqaA) 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 https://github.com/microsoft/TypeScript/issues/33760#issuecomment-537584492 . 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:

```ts
[T] = {
  length: 1
  "0": T
  // and other array's properties
}
```

And `T[]` resembles:

```ts
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](https://www.typescriptlang.org/play?#code/C4TwDgpgBAKlC8UB2BXAtgIwgJwNoF0oAyKXVTHfAWACgAbCYKYALllwEZCB6bqAfkG0GTBkjYxcAIjEBzYAAsp+IA)

```ts
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

<!--
  What do you want to use this for?
  What shortcomings exist with current approaches?
  What workarounds are you using in the meantime?
-->

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`.

```ts
// 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.