Reputation: 3818
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.
Upvotes: 173
Views: 172645
Reputation: 3785
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.
Upvotes: 70
Reputation: 664
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>;
Upvotes: 3
Reputation: 458
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
Upvotes: 6
Reputation: 911
Based on @will-madem's answer, I've done a couple of tweaks:
T
or T[]
T[][][]
=> T
type ArrayElement<MaybeArrayType> =
MaybeArrayType extends readonly (infer ElementType)[]
? (
ElementType extends unknown[]
? ArrayElement<ElementType>
: ElementType
)
: MaybeArrayType;
Upvotes: 1
Reputation: 316
I got the shortest answer.
type A = Array<string>;
type B = A[0]; // string
Upvotes: -4
Reputation: 180
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:
Upvotes: 8
Reputation: 53
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>;
Upvotes: 5
Reputation: 6955
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];
Upvotes: 210
Reputation: 1869
Another alternative:
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never
Upvotes: 99
Reputation: 51809
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; }'
Upvotes: 141