Repository: TypeScript
Issue: feature: ability to extract union of valid index numbers for tuple
Labels: Suggestion,Awaiting More Feedback

<!-- 🚨 STOP 🚨 𝗦𝗧𝗢𝗣 🚨 𝑺𝑻𝑶𝑷 🚨

Half of all issues filed here are duplicates, answered in the FAQ, or not appropriate for the bug tracker. Please read the FAQ first, especially the "Common Feature Requests" section.

-->

## Search Terms

<!-- List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily -->

indexof array indexof tuple restrict argument to tuple index

## Suggestion

<!-- A summary of what you'd like to see added or changed -->

I'd like the ability to restrict a type to a valid index number of a tuple. For example, given the tuple `[string]`, I'd like to extract the type `0`. Given the tuple `[string, number, object]`, I'd like to extract the type `0 | 1 | 2`. 

## Use Cases

<!--
What do you want to use this for?
What shortcomings exist with current approaches?
-->

_[see related S.O. question](https://stackoverflow.com/questions/57509699/how-to-restrict-method-argument-to-index-number-of-tuple-type)_

Currently, if a generic class receives a type argument which is a tuple, it isn't possible to create a method on the class which restricts its argument to a valid index of that tuple.

## Examples

<!-- Show how this would be used and what the behavior would be -->

```ts
class FormArray<T extends [any, ...any[]]> {
  constructor(public value: T) {}

  // how to restrict `I` to only valid index numbers of `T` ?
  get<I extends keyof T>(index: I): T[I] {
    return this.value[index];
  }
}
```

## Implementation ideas

I imagine there are several routes to achieving my goal.

#### Idea 1 (perhaps heavy handed):

Add a new keyword, such as `indexof` which can only be used on arrays and tuples. If indexof is used on an array, it will always return `number`. If indexof is used on a tuple, it returns a union of `0 .... tuple length - 1` (e.g. if the tuple was of length 3 `indexof` would return `0 | 1 | 2`). 

#### Idea 2:

Ability to cast a string type to the equivalent number type. For example, the ability to cast type `"0"` to `0`. This would allow using the `keyof` keyword to get all properties of a tuple, cast property types to numbers (if possible), and then filter out any property types which aren't numbers (i.e. filter out `"length"`, `"splice"`, etc. and leave `0 | 1 | 2`).

For example, [as pointed out in this comment](https://github.com/microsoft/TypeScript/issues/27995#issuecomment-441157546), it is currently possible to get the indexes of a tuple in string form (i.e. `"0" | "1" | "2"`).

```ts
type ArrayKeys = keyof any[];
type Indices<T> = Exclude<keyof T, ArrayKeys>;

Indices<[string, number]>; // "0" | "1"
```

#### Idea 3:

As pointed out in a comment, you can get the index numbers of a tuple using the following type:

```ts
type Indices<T extends {length: number}> = Exclude<Partial<T>["length"], T['length']>;
```

Unfortunately, the result of this type is not considered a `keyof` the input tuple (which results in a type error if you try and use the type as a key for the tuple). If there were some way of using a type assertion to tell the compiler that this is, in fact, a `keyof T`, that might also work.

_note: this type differs from the type presented in **idea 2** (above) because, unlike this type, the type in idea 2 is a `keyof T`_

## Checklist

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, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).