Array type notations: T[] vs. Array<T> in TypeScript

[2025-02-17] dev, typescript
(Ad, please don’t block)

In this blog post, we explore two equivalent notations for Arrays in TypeScript: T[] and Array<T>. I prefer the latter and will explain why.

T[] vs. Array<T>  

In TypeScript, there are two notations for an Array of strings:

  • string[]
  • Array<string>

Both notations are equivalent.

Why are there two notations? Why does TypeScript prefer T[]?  

Why are there two notations?

  • TypeScript 0.9 introduced generics to the language and enabled the notation Array<T>.
  • Before that, T[] was the only notation for Arrays.

So it looks like TypeScript simply stuck with the status quo when version 0.9 came along: It still exclusively uses T[] – e.g., when inferring types or even when displaying a type that was written Array<T>.

Even though I have read once or twice that JSX is the cause of TypeScript’s preference, I don’t think that’s the case:

  • The Array<T> notation is compatible with JSX: It only exists at the type level and can be used without any problems in .jsx files.

  • JSX does, however, make one syntax impossible to use – type assertions via angle brackets:

    // Not possible in .tsx files:
    const value1 = <DesiredType>valueWithWrongType;
    
    // Can be used everywhere:
    const value2 = valueWithWrongType as DesiredType;
    

Why I prefer the notation Array<T>  

I find it often looks better – especially if constructs related to element types get more complicated:

// Array of tuples
type AT1 = Array<[string, number]>;
type AT2 = [string, number][];

// Array of union elements
type AU1 = Array<number | string>;
type AU2 = (number | string)[];

// Inferring type variables
type ExtractElem1<A> = A extends Array<infer Elem> ? Elem : never;
type ExtractElem2<A> = A extends (infer Elem)[] ? Elem : never;

// Readonly types
type RO1 = ReadonlyArray<unknown>;
type RO2 = readonly unknown[];
  // `readonly` applies to `[]` not to `unknown`!

More reasons:

  • Array<T> looks similar to Set<T> and Map<K,V>.
  • T[] can be confused with [T] (tuples that have a single component whose type is T) – especially by people new to TypeScript.
  • If we want to create an empty Array without a type annotation then that syntax is more consistent with the angle bracket type notation:
    const strArr = new Array<string>();
    

One point in favor of T[]  

Because TypeScript always uses T[], code that uses that notation is more consistent with language tooling.

Syntactic caveat: The [] in T[] binds strongly  

The square brackets in T[] bind strongly. Therefore, we need to parenthesize any type T that consists of more than a single token – e.g. (line A, line B):

type ArrayOfStringOrNumber = (string | number)[]; // (A)

const Activation = {
  Active: 'Active',
  Inactive: 'Inactive',
} as const;
type ActivationKeys = (keyof typeof Activation)[]; // (B)
  // ("Active" | "Inactive")[]

Linting Array notations  

typescript-eslint has the rule array-type for enforcing a consistent Array notation style. The options are:

  • Always T[]
  • Always Array<T>
  • T[] for single-token T; otherwise Array<T>