Type Aliases
- #typescript
- #type-aliases
- #custom-types
In questa lezione
1. Introduction
A type alias, declared with the type keyword, gives a name to any type expression — a primitive, a tuple, an object shape, a function signature, or a union of several types. It doesn’t create a new type; it just gives an existing type a reusable, readable name.
type Coordinate = [number, number]; // custom type
Coordinate is a tuple type: a fixed-length array where each position has its own specific type — here, exactly two numbers, always in that order. Without the alias you’d have to repeat [number, number] everywhere you needed this shape; with it, you write Coordinate once and reuse the name.
2. Using a type alias
Once declared, a type alias can be used anywhere a type annotation is expected — function parameters, return types, variable declarations, array element types, and so on.
function compareCoords(p1: Coordinate, p2: Coordinate): Coordinate {
return [p1[0], p2[1]];
}
const coords: Coordinate[] = [];
compareCoords takes two coordinates and combines the x of the first with the y of the second, returning a new Coordinate. coords is declared as Coordinate[] — an array of tuples — reading far more clearly than [number, number][] would have.
Suggerimento
Tuples are stricter than plain arrays: p1[0] and p1[1] are individually typed as number, and TypeScript knows the tuple has exactly two elements — accessing p1[2] is a compile error, whereas a plain number[] would silently allow it (and just report undefined at runtime).
3. What else can a type alias name?
Tuples are just one example. A type alias can name essentially any type expression:
3.1 Primitives and unions
type ID = string | number;
function findUser(id: ID) {
// id could be a numeric database ID or a string UUID
}
ID is a union type: a value that is either a string or a number. This is something a plain interface cannot express at all — interfaces only describe object shapes, while unions can combine any types, including primitives.
3.2 Function types
type Handler = (event: string) => void;
function onClick(handler: Handler) {
handler("click");
}
const logHandler: Handler = (event) => console.log(`Event received: ${event}`);
onClick(logHandler);
Handler names an entire function signature — a callback that receives a string and returns nothing. Naming function types like this makes APIs that accept callbacks much easier to read than repeating (event: string) => void at every call site.
3.3 Object shapes
type Point3D = {
x: number;
y: number;
z: number;
};
function distanceFromOrigin(p: Point3D): number {
return Math.sqrt(p.x ** 2 + p.y ** 2 + p.z ** 2);
}
This looks just like an interface would, and for a simple object shape like this, either works equally well.
4. type vs interface, revisited
We touched on this briefly in the Interfaces lesson. Now that you’ve seen tuples, unions, and function types, the practical distinction is clearer:
- A
typealias can name anything: primitives, unions, tuples, function signatures, and object shapes. - An
interfacecan only describe object shapes (including callable/method signatures on that object), but it supportsextendsand declaration merging (re-opening the same interface name to add more members later), which atypealias cannot do.
// This is only possible with `type` — there is no equivalent interface form:
type Result = { success: true; value: number } | { success: false; error: string };
function handleResult(result: Result) {
if (result.success) {
console.log(result.value);
} else {
console.log(result.error);
}
}
Nota
This pattern — a union of object shapes distinguished by a shared literal field like success — is called a discriminated union, and it’s one of the most powerful and common uses of type aliases in real TypeScript code. TypeScript automatically narrows result to the correct branch inside each if, based on the value of success.
Attenzione
A common mistake is trying to “extend” a type alias with the same extends syntax used for interfaces. Type aliases don’t support extends — instead you combine them with an intersection (&): type Employee = Person & { employeeId: number };. The end result is similar, but the syntax and mental model differ.
5. Further reading
- TypeScript Handbook — Object Types (aliases and interfaces)
- TypeScript Handbook — Narrowing (discriminated unions)
- TypeScript Playground
6. Quiz
Mettiti alla prova
0/6 risposte
What does
type Coordinate = [number, number];declare?What kind of type is
type ID = string | number;?Why can't a plain
interfaceexpress something liketype ID = string | number;?What does
type Handler = (event: string) => void;name?How do you combine two type aliases into one that has all members of both?
What is a 'discriminated union' commonly used for?
7. Exercises
7.1 Exercise
Scenario: You’re modeling geometric data and simple event callbacks for a small graphics utility library.
Task:
- Define a
type Coordinate = [number, number];and a functionmidpoint(a: Coordinate, b: Coordinate): Coordinatethat returns the midpoint of two coordinates. - Define a
type ShapeId = string | number;and a functionlogShapeId(id: ShapeId): voidthat logs the id along with whether it came in as a string or a number (hint: usetypeof). - Define a
type ResizeHandler = (width: number, height: number) => void;and write a functiononResize(handler: ResizeHandler)that calls the handler with sample values.
Learning objective: practice declaring and using type aliases for tuples, unions, and function signatures.
7.2 Exercise
Scenario: You’re building a small API-response modeling layer and want to represent success/failure without throwing exceptions.
Task:
- Define
type ApiResult<T> = { success: true; data: T } | { success: false; error: string };(a generic discriminated union — combining what you learned about generics with type aliases). - Write a function
fetchUser(id: number): ApiResult<{ name: string; age: number }>that returns a failure result ifid <= 0, and a success result with a fake user otherwise. - Write a function
printResult(result: ApiResult<{ name: string; age: number }>): voidthat uses anif (result.success)check to safely narrow and print either the user’s name or the error message. - Explain in a comment why accessing
result.dataorresult.errorwithout first checkingresult.successwould be a compile error.
Learning objective: combine generics and discriminated unions built with type aliases to model success/failure results safely.