178

Say I have some array type T[], is it possible to extract the type T within another alias / interface? For example my (fake) ideal code would be as follows:

// for illustration only...

type ArrayElement<T[]> = T;

// then, ArrayElement<string[]> === string

If no, are there general type theory reasons for not allowing such an operator? If no again, I might suggest it be added.

9 Answers 9

214

Update: based on @jerico's answer below

The following type alias will return the type of the elements in an array or tuple:

type ArrayElement<ArrayType extends readonly unknown[]> = 
  ArrayType extends readonly (infer ElementType)[] ? ElementType : never;

So these examples would work:

type A = ArrayElement<string[]>; // string
type B = ArrayElement<readonly string[]>; // string
type C = ArrayElement<[string, number]>; // string | number
type D = ArrayElement<["foo", "bar"]>; // "foo" | "bar"
type E = ArrayElement<(P | (Q | R))[]>; // P | Q | R

type Error1 = ArrayElement<{ name: string }>; 
//                         ^^^^^^^^^^^^^^^^
// Error: Type '{ name: string; }' does not satisfy the constraint 'readonly unknown[]'.

Explanation

The type guard (the bit in the angle brackets) ArrayType extends readonly unknown[] says that we expect the type parameter ArrayType to be at least a readonly array (it also accepts a mutable array) so that we can look at its element type.

This prevents passing in a non-array value, as in the final example, which prevents ArrayElement ever returning never.

Note that readonly unknown[] is syntax added in TypeScript 3.4; for earlier versions use ReadonlyArray<unknown>.

On the right-hand side, the conditional expression asks the compiler to fill in the value of ElementType in the pattern readonly ElementType[] and return ElementType if it can, or never if it can't.

Since the type guard at the beginning means we will only ever be passed a value which matches this pattern, it's guaranteed always to match and never to return never.

Previous answer

type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType[number];
Sign up to request clarification or add additional context in comments.

5 Comments

This implementation infers the element type from the array type, while the accepted answer infers the type of the first element in the array. I just thought it was more elegant, but since the array is guaranteed (by its type definition) to only contain elements of the same type, there's no technical advantage to using this solution.
Typescript supports indexed based array element types such as: [number,string]. So this answer has some advantages as ArrayElement<[number,string]> -> number | string
this should no longer be the accepted answer, as described here
I agree with @feihcsim. My answer may have issues if the --noUncheckedIndexedAccess flag is enabled, whereas @jerico's answer wouldn't. That'd be enough of a reason for me to choose that answer over my one, which is a little sad since ArrayType[number] is much easier to read, especially for beginners.
@feihcsim I'm pretty sure this answer did not need to change and the original answer (ArrayType[number]) still works fine, even with --noUncheckedIndexedAccess enabled. That flag does not affect these type-level operations.
141

Since 2.1, typescript supports [ ] operator for types. The official name is indexed access types, also called lookup types, and it works like this:

type A = {a: string, b: number} [];

type AElement = A[0];

let e: AElement = {x: 0}; //error TS2322: Type '{ x: number; }' is not 
                       //assignable to type '{ a: string; b: number; }'

5 Comments

What's happen when a be nullable ?
Same or similar error because type { x: number; } is still not assignment-compatible with the type of array element
It's also possible to do YourArray[number] which I think is more general than YourArray[0]
YourArray[number] is indeed the canonical solution.
A type error as an example completely neglects what you're trying to showcase. It would better be: let e: AElement = { a: 'A', b: 1 }; // ok
102

Another alternative:

type ArrayElement<A> = A extends readonly (infer T)[] ? T : never

2 Comments

this is actually the only one true "correct" answer. i believe it's for this reason that the TypeScript team is going to be releasing a --noUncheckedIndexedAccess flag
it's also the only working way for complex types such as P[] | (Q | R)[]
71

This can be achieved pretty easily by writting index type in brackets:

type ArrayElementType = ArrayType[number];

Update

As a comment below suggests, to create a generic array element type:

type ArrayElementType<ArrayType extends  Array<T>, T = any> = ArrayType[number];

P.S. number is not a value but a type.

4 Comments

Or as a generic type: type ArrayElementType<A extends Array> = A[number]
This should be the accepted answer.
The generic version doesn't work for me. TypeScript gives error: Generic type 'Array<T>' requires 1 type argument(s).
Managed to make it work with the following code: type ArrayElementType<ArrayType extends Array<T>, T = any> = ArrayType[number]; But it's no more a nice short answer. Plus it has this extra type parameter T.
8

Another option:

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

You can find more about infer at: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#inferring-within-conditional-types:

Comments

6

Using conditional types this snippet flattens array types to their element types, or leaves them alone otherwise

type Flatten<T> = T extends any[] ? T[number] : T;

Implementing the snippet above type Str below will have a type of string

// Extracts out the element type.
type Str = Flatten<string[]>;

and type Num below will remain it's original type number

// Leaves the type alone.
type Num = Flatten<number>;

In summary when Flatten is given an array type, it uses an indexed access with number to fetch out string[]’s element type. Otherwise, it just returns the type it was given. This example was sourced from the Typescriptlang handbook on conditional types

1 Comment

FWIW, this is working well in my case... the first example of an array type.
5

The utility-type library has a type for that https://github.com/piotrwitek/utility-types#valuestypet (along with many others)

For example

import { ValuesType } from 'utility-types';

type NumberArray = number[];
// Expect: number
type NumberItems = ValuesType<NumberArray>;

1 Comment

How does one use it? Can you edit your post and give an example?
3

The type-fest library calls this an IterableElement<T> and it allows to extract the element type of any Iterable or AsyncIterable, including arrays and generators:

type MyArray = Array<number>;
type MyArrayElements = IterableElement<MyArray>;

Comments

1

Based on @will-madem's answer, I've done a couple of tweaks:

  • The generic might be an array, or not: T or T[]
  • It will remove multiple "levels" of array: T[][][] => T

Full example here.

type ArrayElement<MaybeArrayType> = 
  MaybeArrayType extends readonly (infer ElementType)[]
    ? (
        ElementType extends unknown[]
            ? ArrayElement<ElementType>
            : ElementType
    )
    : MaybeArrayType;

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.