Skip to main content

Indexed Access Types

Indexed access types extract the type of a property from an object, array, or tuple type.

1type Person = {name: string, age: number};2type Age = Person['age']; // number

When to use this

Use indexed access types to derive a property's type from an existing type rather than duplicating it. This keeps types in sync — if the source type changes, the extracted type updates automatically. For transformations across all keys, use mapped types instead.

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 elementsinvalid-tuple-indexCannot access number literal 2 on Pair because Pair [1] only has 2 elements, so index 2 is out of bounds.

The index can be a union, including the result of calling keyof:

1type Cat = {2  name: string,3  age: number,4  hungry: boolean,5};6
7type CatValues = Cat[keyof Cat]; // type CatValues = string | number | boolean

The index can also be a generic:

1function getProp<O extends {+[string]: unknown}, K extends keyof 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`incompatible-typeCannot assign getProp(...) to y because number [1] is incompatible with string [2].7getProp({a: 42}, 'b'); // Error - `b` does not exist in object typeprop-missingCannot call getProp because property b is missing in object literal [1].prop-missingCannot call getProp because in type argument K: property b is missing in object literal [1].

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:

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 NonNullable<...> 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

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,

See Also

  • Objects — the primary type indexed access is used with
  • Mapped Types — transforming object types, often combined with indexed access
  • Conditional Types — type-level conditionals, frequently used alongside indexed access
  • Utility Typeskeyof, Pick, and other built-in type helpers