Skip to main content

Type Aliases

Type aliases give a reusable name to any type. They are fully transparent — interchangeable with the type they refer to.

1type ID = number;2const userId: ID = 42;

When to use this

Use type aliases to give descriptive names to complex or frequently used types. Since aliases are transparent, any code that accepts the underlying type also accepts the alias and vice versa. When you need to hide the underlying type from consumers of a module, use opaque type aliases instead. Interfaces, classes, and enums also introduce named types with additional capabilities beyond simple naming.

Type Alias Syntax

Type aliases are created using the keyword type followed by its name, an equals sign =, and a type definition.

type Alias = Type;

Any type can appear inside a type alias.

1type NumberAlias = number;2type ObjectAlias = {3  property: string,4  method(): number,5};6type UnionAlias = 1 | 2 | 3;7type AliasAlias = ObjectAlias;

Type Alias Generics

Type aliases can also have their own generics.

1type MyObject<A, B, C> = {2  property: A,3  method(val: B): C,4};

Type alias generics are parameterized. When you use a type alias you need to pass parameters for each of its generics.

1type MyObject<A, B, C> = {2  foo: A,3  bar: B,4  baz: C,5};6
7const val: MyObject<number, boolean, string> = {8  foo: 1,9  bar: true,10  baz: 'three',11};

Recursive Type Aliases

A type alias can refer to itself, as long as the cycle passes through a constructor like an array, object, or function type. This is the canonical way to model tree- and list-like shapes:

1type Tree = {2  value: number,3  children: Array<Tree>,4};5
6const t: Tree = {7  value: 1,8  children: [9    {value: 2, children: []},10    {value: 3, children: [{value: 4, children: []}]},11  ],12};

Mutual recursion between aliases works as well, provided every cycle is broken by a constructor:

1type Doc  = {nodes: Array<Node>};2type Node =3  | {kind: "text",  text: string}4  | {kind: "group", doc: Doc};

Type aliases are erased

A type declaration introduces no runtime binding — it only exists at compile time. You cannot use a type alias name in a value position, such as an argument, a typeof expression, or an instanceof check:

1type Status = "ok" | "error";2
3const s: Status = "ok"; // Works!4const v = Status;       // Errortype-as-valueCannot use type Status [1] as a value. Types are erased and don't exist at runtime.

This is unlike classes and Flow Enums, which introduce a runtime value alongside a type of the same name.

See Also

  • Opaque Type Aliases — type aliases that hide their underlying type outside of the defining file
  • Generics — parameterized types used with type aliases and functions
  • Interfaces — another way to define reusable types, with structural (shape-based) checking for class instances
  • Const Expressionsas const for producing runtime values that double as types