typescript

Typescript: Retrieve element type information from array type


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.


Solution

  • 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];