All Articles

This Crazy Syntax Lets You Get An Array Element's Type

Matt Pocock
Matt PocockMatt is a well-regarded TypeScript expert known for his ability to demystify complex TypeScript concepts.

Let's talk about another crazy TypeScript trick: Array[number].

const roles = ["admin", "editor", "contributor"] as const;

type RolesAsType = typeof roles;

type Role = RolesAsType[number];
type Role = "admin" | "editor" | "contributor"

It looks like absolute insanity.

But I promise by the end of this email, you'll know how it works.

Extracting Types From Arrays

Let's start with a slightly simpler example.

First, we'll define an array of some fixture data we might use in some tests.

const possibleResponses = [
  {
    status: 200,
    body: "Hello, world!",
  },
  {
    status: 404,
    body: "Not found",
  },
  {
    status: 500,
    body: "Internal server error",
  },
];

We can use typeof on possibleResponses to extract its type.

We can see that it is an array of objects with a status and body property.

But what if we want to remove the 'array' part of the type and just get to the object inside?

type PossibleResponses = typeof possibleResponses;
type PossibleResponses = { status: number; body: string; }[]

We can use this arcane-looking trick, Array[number], to extract the type of the array.

type PossibleResponse = PossibleResponses[number];
type PossibleResponse = { status: number; body: string; }

Why does this work? What even is this syntax?

Indexed Access Types

Well, the syntax is an indexed access type. It's the way to access a property on a type.

A simpler version would be to grab a property from an object:

type NavbarProps = {
  onChange: () => void;
};

type OnChangeType = NavbarProps["onChange"];
type OnChangeType = () => void

By passing number to the indexed access type, we're saying "give me all the properties of the object that are accessed with numeric keys".

To demonstrate this, we can create an object with string keys, but a numeric index signature:

type ExampleObj = {
  // String keys
  stringKey1: "string-key";
  stringKey2: "string-key";

  // Numeric index signature
  [key: number]: "number-key";
};

We can then use ExampleObj[number] to extract only the numeric values from the object.

type ExampleObj = {
  // String keys
  stringKey1: "string-key";
  stringKey2: "string-key";

  // Numeric index signature
  [key: number]: "number-key";
};

type NumericValuesOnly = ExampleObj[number];
type NumericValuesOnly = "number-key"

What's The Point?

Keen observers might be looking at this with scepticism. Is this trick really useful?

Surely, you would just create the type first, then use it in your application.

No need to extract it from an array.

type Response = {
  status: number;
  body: string;
};

const possibleResponses: Response[] = [
  {
    status: 200,
    body: "Hello, world!",
  },
  {
    status: 404,
    body: "Not found",
  },
  {
    status: 500,
    body: "Internal server error",
  },
];

Well, this trick comes into its own in a few places.

It goes without saying that it's useful in library code.

But it's also great when you're building your own enums from arrays, like in the first example.

const roles = ["admin", "editor", "contributor"] as const;

type RolesAsType = typeof roles;

type Role = RolesAsType[number];
type Role = "admin" | "editor" | "contributor"

By the way, this example works because roles is marked as const, which makes TypeScript infer the literal values of the array.

Without it, TypeScript would infer the type as string[]:

const roles = ["admin", "editor", "contributor"];

type RolesAsType = typeof roles;

type Role = RolesAsType[number];
type Role = string

Deriving types from values is an extremely powerful concept - so much so that I wrote an entire chapter of my book on it.

So, that's Array[number] in TypeScript. It extracts all the numeric keys from an object.

In the case of an array, that means it extracts the type of the array member.

Matt's signature

Share this article with your friends