Topic 3.1
Type Aliases
In one line
A type alias gives a name to any type with type Name = ..., so you can write a long or important type once and reuse it. The name is only a label: it does not create a new, different type.
Think of it like this
A contact in your phone. Instead of dialling "+91 98765 43210" every time, you save it as "Mum". The name is a shortcut to the same number, and if the number changes you fix it in one place. A type alias is a saved contact for a type. You write the long description once, give it a name, and use the name everywhere.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Type alias
- A name you give to a type with
type Name = .... It is a label, not a new kind of value. - Annotation
- The
: Typeyou write after a variable or parameter to say what it holds. - Object type
- A type written with braces, like
{ x: number; y: number }, that lists the properties a value must have. - Union
- A choice between types written with
|, such asstring | number. Topic 4.1 covers it in full. - Literal type
- A type that allows exactly one value, like
"open"or42. Topic 4.2 covers it. - Tuple
- An array with a fixed length where each position has its own type, like
[string, number]. Topic 5.2 covers it. - Type parameter
- A blank in a type, written in angle brackets like
<T>, that the user of the type fills in. - Erased
- Removed before the program runs. All types, including aliases, are erased, so they cost nothing at runtime.
- PascalCase
- A naming style where every word starts with a capital letter and there are no spaces:
UserId,HttpMethod.
Step by step
01Save a type under a name
Write type, a name, =, then the type. From then on the name stands for the whole type.
Here Point is the object shape and UserId is a primitive. Both are used as ordinary annotations afterwards.
type UserId = string;
type Point = { x: number; y: number };
const id: UserId = "u-42";
const origin: Point = { x: 0, y: 0 };02Name a choice
Aliases are not only for objects. A union of exact texts becomes a tiny "menu" of allowed values, which is how many programs replace magic strings.
Putting any other text into a Status variable is an error, and the editor even suggests the two allowed values while you type.
type Status = "open" | "closed";
let status: Status = "open";
status = "closed"; // fine
status = "paused"; // error TS2322: Type '"paused"' is not assignable to type 'Status'.03An alias is not a new type
Two aliases of the same type are interchangeable. Meters and Feet are both number, so assigning one to the other is allowed even though mixing metres and feet is a real-world bug.
Use aliases to make intent visible to humans. If you need the checker to enforce the difference, you need more than an alias.
type Meters = number;
type Feet = number;
const height: Meters = 2;
const converted: Feet = height; // allowed: both are just number04Reuse and parameters
A function type is awkward to repeat, so it earns a name. A type parameter lets one alias work for many types: Pair<T> is a tuple of two Ts, Maybe<T> is a T or null.
Writing Pair<number> fills the blank: it means [number, number].
type Callback = (error: Error | null, data?: string) => void;
type Pair<T> = [first: T, second: T];
type Maybe<T> = T | null;
const nickname: Maybe<string> = null;
const swap = <T>(p: Pair<T>): Pair<T> => [p[1], p[0]];05Aliases live only in the checker
When TypeScript removes types, every alias goes with them. The JavaScript has no trace of Point or Status.
That is why an alias is free at runtime, and why you cannot use an alias name where a value is expected.
Try it yourself
- 1
Add a third status
In the first example, change the alias to
type Status = "open" | "closed" | "paused";. Predict what happens to the red error before you look.The error disappears:
"paused"is now one of the allowed texts. You edited one line and every use ofStatusfollowed. - 2
Make Feet a string
In the second example, change
type Feet = number;totype Feet = string;. Predict which lines turn red.Now
MetersandFeetreally differ, so four errors appear. Line 5,const converted: Feet = height;, puts a number into a string. Line 9, intoFeet, returns a number where a string was promised. On line 12 the calltoFeet(converted)passes a string where a number is expected, and.toFixeddoes not exist on a string. One edit to the alias reached every line that used it. - 3
Fill the blank
In the third example add
const names: Pair<string> = ["a", "b", "c"];. Read the error: which length does the alias demand?It says
Source has 3 element(s) but target allows only 2.Pairis a tuple of exactly two, so a third element is refused.
Code & diagrams
Notice the Types panel: the object shape and the union keep their alias names, but UserId is shown as plain string. The last line is the error. The code still runs, so the console shows what happened before it.
Console
(nothing was printed)Expected console output
u-42 closed 5No error: a Meters value goes into a Feet variable because both are number. The hover shows number, not Feet. Aliases document; they do not fence.
Console
(nothing was printed)Expected console output
6.56
trueThe arrow function passed to load has no annotations: TypeScript takes err and data from the Callback alias. Hover over them in your editor to see.
Console
(nothing was printed)Expected console output
null ready
[ 2, 1 ] nullBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Make two aliases really different
In the second example change type Meters = number; to type Meters = string;.
Break #2
Use a type as a value
At the very end of the first example, add the line const make = Point;.
Myth vs fact
Myth
A type alias creates a new, separate type.
Fact
It only creates another name for the same type. Two aliases of number are fully interchangeable.
Myth
Aliases are only for object shapes.
Fact
Any type can be aliased: primitives, unions, functions, tuples, arrays, even other aliases with type parameters.
Myth
Aliases cost something at runtime.
Fact
They are erased completely. The compiled JavaScript contains no trace of them.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
When TypeScript prints a type, it keeps an alias name if the type came from one in a way it can track (object shapes, unions, instantiated generics) and drops it for primitives. This is why hover text is sometimes
Pointand sometimes a long expansion. It is display only; assignability never depends on the name. - ▸
Aliases can be recursive when the recursion goes through an object property, array or tuple:
type Tree = { value: number; children: Tree[] }. A direct self-reference liketype Bad = Bad | stringis an error (TS2456: Type alias 'Bad' circularly references itself). Recursive conditional types were added in TypeScript 4.1. - ▸
Do not alias everything. A name earns its place when it carries meaning (
UserId), when the type is long, or when it is shared.type Name = stringused once in one function only makes readers jump to its definition.
Remember this
- 1
The problem this solves: a type such as
(error: Error | null, data?: string) => voidis long, and writing it in ten places means ten places to fix when it changes. The syntax istype Name = SomeType;. After it,Namecan be used anywhere a type goes.type UserId = string;makesUserIdmeanstring.type Point = { x: number; y: number };makesPointmean "an object with a numberxand a numbery".type Status = "open" | "closed";names a choice between two exact texts. The word on the right can be any type: a primitive, an object shape, a union, a function, a tuple, an array. - 2
An alias is just a name, not a new type.
type Meters = number;andtype Feet = number;are bothnumber, so TypeScript happily lets you put aMetersvalue into aFeetvariable (the second example shows this). Aliases make code easier to read (UserIdsays more thanstring) but do not protect you from mixing things up. Real protection takes a trick called branding (Topic 10.4). - 3
Because an alias disappears when the checker looks inside, hover text often shows the alias name for object shapes and unions (
Point,Status) but the underlying type for plain primitives (const id: string, notconst id: UserId). Nothing is wrong: for a primitive there is nothing to keep, so the name is dropped. - 4
Aliases shine when a type is long or used in many places. Compare
(error: Error | null, data?: string) => voidrepeated in ten functions withCallback. If the shape changes you edit one line, and the compiler then points at every place that no longer fits. They also give names to ideas:Status,Colour,Pair<string>. - 5
Aliases can take type parameters, the same idea as function parameters but for types:
type Maybe<T> = T | null;and thenMaybe<string>meansstring | null. You will study generics in depth in Phase 7; for now, read<T>as "a blank to fill in when you use it". - 6
Two limits. An alias exists only for the checker: it is erased from the JavaScript, so you can never use
Pointas a value (const x = Pointis an error, see Break it). And an alias name must be unique in its scope: declaringtype Configtwice is an error (aninterfacebehaves differently, Topic 3.4). Name aliases inPascalCase(UserId,HttpMethod) so they look different from variables.
Explain it without notes
What is a type alias, and what can it name?
If I write type Meters = number and type Feet = number, can I pass a Meters where a Feet is expected? Why?
Why can't you use a type alias as a value at runtime?
When is an alias worth creating?
Practice
Write a type alias Direction that allows only the texts "up", "down", "left" and "right", and a function turn(d: Direction): Direction that returns the opposite direction. Show what TypeScript says for turn("north").
Create an alias Pair<T> for two values of the same type and a function first that returns the first element of a Pair. Call it with numbers and with texts and write down the inferred return types.
Trade-offs
- ↔
Aliases improve readability and give one place to change a type, but they add a name readers must look up. Use them for shared or meaningful types, not for every annotation.
- ↔
Because an alias is only a label, it never prevents mixing two ideas that share a base type. Do not rely on
type UserId = stringto stop aProductIdslipping in.
Done when you can
I can write an alias for a primitive, an object shape, a union and a function type.
I can explain why
MetersandFeet(bothnumber) are interchangeable.I can explain why an alias cannot be used as a runtime value.
I can write a generic alias like
Maybe<T>and fill it in.I can decide whether a type deserves a name.