What's going on with index types in TypeScript?
Tags: typescript
Published: 2020-04-08 06:51:02

[Playground link for all code here.](https://www.typescriptlang.org/play/#code/FAFwngDgpgBAYgewTAvDA3sG2CGAuGAZxACcBLAOwHMBuLbAIwIoFcBbBqEu7GARmYsANkLoBfYKEiwAgqnhIA2gCIcygLo0YAem3Fy1GFOgwAQvMQIVDDVt2sOXY7ADyDAFYuKsNJcV9NHW1WES1JZxgAFQQAZVJKKgslZRBY+OpbIIAKAEpUAD4idKoIgDUcIRYoQiSrZQA3CqrCTN0ABRIEaBJwGAByRsrqvpgAEwRqmAoEEBgoAA8yYhgEChhwEz7LPoA6LIAmAGZDgE4c8I3YACUoADM3dygAYxBTMEjpADl2ThJaxQcv0Cug+m22MAAFjgatMYGwcCAnhCEjBKKMFkQyFQKAiWCRYLcEH9Lv1AVxdgcAKyHADs5xJN3uHmer3e0jiBkSviU+gSwO0oNgWyQIyhMOQ8MRyMMaIxhCxOJAeIJRPW0lJP3Je321Lp4UkJIAsmAZCQSDgwPJFLzqAAaKaakj2m1UTQG9UALS4yDQxtN5rAigADPyg0YSV4fDA-WaLf5+cpVlBlBd1TdRiwnlGYwGVPiM1nWtoGVAC1H0LwYFknhUhAwcE8ANa3CgELIQfH1MgIFiEcpDAhhgA+MET3mUMBHO2n0-D6oAMlBqCAIfIc3HlEIl1QVxogod3SZGf6LW9Bd9HH9fSbY4GySR+S7Jw7LxFj7ez+zimub7mXfzBX6dcwFFaEpglBEkRRWV5kxbFcXxGBCWJdU+hdCkdVpc59RJUwcCvDB6BgRRGygMACH-CjiltYAxDCEtbjwkhP2gDkUTQJjrWKR9ikPWAAGEoQAL3kdASLIwRL3USTfjoucjzuQScCElioAvX55CUoSAUdfl7z4mAABFVkbUSiPEsB8CKTlpOshIeGwCymBfIEZK4OxtAvLgyCeVEKHRWCST6e8RiWcDZmheV4IYLd1mQJ8YLVTZ0O1AAWPhDmwiQIgAUSgCAzN4VRlDc7gjGyhi8ogN4AGkyLkNAqpUNQ9MdN87iq1S2MMRr8q4myPKiVCqtA8U4Ug6VEkSqLFWVJDVSClKqSw4AgA)

In TypeScript you can reference the _type_ of an object property using the  square bracket notation. 

eg: 

```typescript


type Foo = {
   a: string;
   b: number;
   1: null;
}

type A = Foo["a"]; //string 
type B = Foo["b"]; //number
type ObjOne = Foo[1]; //null; 

```

This also works for arrays

```typescript

type MyArray = [string, number, string];

type Zero = MyArray[0]; //string 
type One = MyArray[1]; //number
```

Now according to the [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html) the `keyof` keyword returns a union type of all of those possible keys, which also includes the Array.prototype methods such as `reduce`, `map` etc. 

```typescript
type Reduce = MyArray["reduce"]; //type Reduce = {    (callbackfn: (previousValue: 0 | "one" | ..... 
type Length = MyArray["length"] //3

```

This works for ordinary objects too, but there aren't many useful native methods that exist on an object instance's prototype (Object.values etc exist as static functions on the class prototype): 
```typescript
type ToString = Foo["toString"]; //() => string
type Values = Foo["values"]; //Property 'values' does not exist on type 'Foo'.(2339)
```

Now where this gets interesting is that as well as referencing the types of properties via a string or number literal as an index - we can also reference them by a given _type_ as an index. However, in this current example, it only works for the `number` type on arrays only: 

```typescript
type RefObjectByTypeNumber = Foo[number]; //Type 'Foo' has no matching index signature for type 'number'.(2537)
type RefObjectByTypeString = Foo[string]; //Type 'Foo' has no matching index signature for type 'number'.(2537)
```

```typescript
type RefArrayByTypeNumber = MyArray[number]; //string | number
type RefArrayByTypeString = MyArray[string]; //Type 'MyArray' has no matching index signature for type 'string'.(2537)
```

If we declare an object by explicitly declaring the types of the keys, this does work: 

```typescript
type Bar = {
   [key: string]: string,
}; 
type RefBarByTypeString = Bar[string]; //string

type Chaz = {[key: number]: number}; 
type RefChazByTypeNumber = Chaz[number]; //number
```

If we declare the object keys directly as string literals, this does not work: 

```typescript
type Eep = {
   "a": number; 
}
type RefEepByKeyA = Eep["a"]; //number
type RefEepByTypeString = Eep[string]; // Type 'Eep' has no matching index signature for type 'string'.(2537)
```

It's not possible to declare both types as keys: 

```typescript
type Donk = {
   [keya: string]: string;
   [keyb: number]: number; //Numeric index type 'number' is not assignable to string index type 'string'.(2413)
}
```

Not that I necessarily want to do that. 


So my main question here  is - **what's actually going on with how we can use types as an index - but only in specific circumstances?**

The documentation from Typescript isn't particularly clear here - the only references in the documentation that I can find is is this part on index types: 
https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types

which doesn't actual demonstrate using a type as an index, 

and the example given here in the `keyof` documentation: 

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html