Skip to main content

Indexed Access Types

Flow’s Indexed Access Types allow you to get the type of a property from an object, array, or tuple type.

Usage

Access an object type's property:

1type Cat = {2  name: string,3  age: number,4  hungry: boolean,5};6
7type Hungry = Cat['hungry']; // type Hungry = boolean8const isHungry: Hungry = true; // OK - `Hungry` is an alias for `boolean`9
10// The index can be a type, not just a literal:11type AgeProp = 'age';12type Age = Cat[AgeProp]; // type Age = number13const catAge: Age = 6; // OK - `Age` is an alias for `number`

Access an array type's element, by getting the type at the array's indices (which are numbers):

1type CatNames = Array<string>;2
3type CatName = CatNames[number]; // type CatName = string4const myCatsName: CatName = 'whiskers'; // OK - `CatName` is an alias for `string`

Access a tuple type's elements:

1type Pair = [string, number];2
3const name: Pair[0] = 'whiskers'; // OK - `Pair[0]` is an alias for `string`4const age: Pair[1] = 6; // OK - `Pair[1]` is an alias for `number`5const wrong: Pair[2] = true; // Error - `Pair` only has two elements
5:19-5:19: Cannot access number literal `2` on `Pair` because `Pair` [1] only has 2 elements, so index 2 is out of bounds. [invalid-tuple-index]

The index can be a union, including the result of calling $Keys<...>:

1type Cat = {2  name: string,3  age: number,4  hungry: boolean,5};6
7type Values = Cat[$Keys<Cat>]; // type Values = string | number | boolean

The index can also be a generic:

1function getProp<O: {+[string]: mixed}, K: $Keys<O>>(o: O, k: K): O[K] {2  return o[k];3}4
5const x: number = getProp({a: 42}, 'a'); // OK6const y: string = getProp({a: 42}, 'a'); // Error - `number` is not a `string`
7getProp({a: 42}, 'b'); // Error - `b` does not exist in object type
6:19-6:39: Cannot assign `getProp(...)` to `y` because number [1] is incompatible with string [2]. [incompatible-type]
7:18-7:20: Cannot call `getProp` because property `b` is missing in object literal [1]. [prop-missing]
7:18-7:20: Cannot call `getProp` because property `b` is missing in object literal [1] in type argument `K`. [prop-missing]

You can nest these accesses:

1type Cat = {2  name: string,3  age: number,4  hungry: boolean,5  personality: {6    friendly: boolean,7    hungerLevel: number,8  }9};10
11type Friendly = Cat['personality']['friendly']; // type Friendly = boolean12const isFriendly: Friendly = true; // Pet the cat

Optional Indexed Access Types

Optional Indexed Access Types work like optional chaining. They allow you to access properties from nullable object types. If before you did:

type T = $ElementType<$NonMaybeType<Obj>, 'prop'> | void;

You can now do:

type T = Obj?.['prop'];

Like optional chaining, the resulting types of Optional Indexed Access Types include void. If you have a long chain of nested optional accesses, you can wrap the entire thing with a $NonMaybeType<...> if you don’t want void in your resulting type.

Example:

1type TasksContent = ?{2  tasks?: Array<{3    items?: {4      metadata?: {5        title: string,6        completed: boolean,7      },8    },9  }>,10};11
12type TaskData = TasksContent?.['tasks']?.[number]?.['items']?.['metadata'];

There is one small difference between optional chaining and Optional Indexed Access Types. If the object type you access is not nullable, the resulting type in optional chaining will not include void. With Optional Indexed Access Types, for implementation reasons, the resulting type will always include void. However, if your object type is not nullable then you don’t need to use an Optional Indexed Access Type, but should just use a regular Indexed Access Type.

Adoption

To use Indexed Access Types, you need to upgrade your infrastructure so that it supports the syntax:

  • flow and flow-parser: 0.155
  • prettier: 2.3.2
  • babel: 7.14

Indexed Access Types are a replacement for the $PropertyType and $ElementType utility types. If you're familiar with those utility types already, here is a quick conversion guide:

  • $PropertyType<Obj, 'prop'> Obj['prop']
  • $ElementType<Obj, T> Obj[T]
  • $ElementType<$PropertyType<Obj, 'prop'>, T> Obj['prop'][T]

We have created an ESLint rule that warns on $ElementType and $PropertyType usage and recommends Indexed Access Types instead. It includes an auto-fixer that can handle most cases. You can simply enable this rule on your codebase and autofix all existing issues.

Install eslint-plugin-fb-flow, and add fb-flow to your ESLint plugin list. Then enable the rule in your ESLint config:

'fb-flow/use-indexed-access-type': 1,