TypeScript Type Aliases
A practical guide to TypeScript type aliases, union types, intersection types, and literal values.
TL;DR
- Declare custom type identifiers using the
typekeyword syntax. - Combine multiple candidate shapes using union pipe
|operators. - Merge distinct object properties together using intersection
&operators.
Type Alias Syntax
type keywordBinds a custom name to any valid type expression.
type Identifier = string | number;
const userId: Identifier = 'usr_981';object type aliasDefines structured object shapes with typed attributes.
type Coordinate = {
x: number;
y: number;
};
const origin: Coordinate = { x: 0, y: 0 };tuple type aliasModels fixed-length arrays with indexed type elements.
type KeyValuePair = [string, number];
const entry: KeyValuePair = ['port', 8080];Union Types
primitive unionPermits values matching any of the specified types.
type InputValue = string | number | boolean;
let current: InputValue = 'active';narrowing with typeofDifferentiates union members inside conditional logic.
function printId(id: string | number) {
if (typeof id === 'string') {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed(2));
}
}string literal unionRestricts assignments to an enumerated set of strings.
type Status = 'pending' | 'success' | 'failed';
const requestStatus: Status = 'pending';Intersection Types
object intersectionMerges multiple object contracts into a combined type.
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;
const sam: Person = { name: 'Sam', age: 28 };extending with &Appends additional fields onto an existing type alias.
type User = { id: string; email: string };
type Admin = User & { permissions: string[] };never resolutionEvaluates incompatible primitive intersections to never.
type Impossible = string & number; // never
// let x: Impossible; // Cannot assign any valueLiteral Types
boolean literalLocks a type strictly to true or false values.
type Truthy = true;
const isVerified: Truthy = true;numeric literalConstrains numbers to explicit predefined constants.
type HttpSuccess = 200 | 201 | 204;
const statusCode: HttpSuccess = 200;as const assertionInfers deeply immutable literal types from objects.
const config = {
endpoint: '/api/v1',
retries: 3
} as const;Generic Type Aliases
generic typeAccepts type arguments to build reusable container types.
type Result<T> = {
data: T;
error?: string;
};
const response: Result<number> = { data: 42 };nullable helperCreates a reusable null or undefined wrapper type.
type Nullable<T> = T | null | undefined;
let cachedToken: Nullable<string> = null;function type aliasAnnotates reusable callback signatures concisely.
type Predicate<T> = (item: T) => boolean;
const isEven: Predicate<number> = (n) => n % 2 === 0;Tips
- Use
typealiases for primitives, unions, and intersections whereinterfacesyntax cannot be applied. - Append
as constto configuration objects to lock properties down into strict read-only literal types.
Warnings
- Remember that
typealiases cannot undergo declaration merging, so declaring duplicate names throws a compiler error. - Intersecting primitive types like
string & numberresults in the uninhabitablenevertype rather than a combined value.
In Practice
Combines literal unions, object intersections, and generic aliases into a type-safe event dispatcher.
- Declare a string literal union representing distinct notification channels.
- Define discriminated message payloads for email and SMS alerts.
- Combine shared audit metadata with payload bodies using intersection types.
- Write a dispatcher function narrowing message channels safely.
type Email = {
channel: 'email';
to: string;
};
type Sms = { channel: 'sms'; phone: string };
type Audit = { timestamp: number };
type Message = (Email | Sms) & Audit;
function send(msg: Message) {
if (msg.channel === 'email') {
console.log(`Email to ${msg.to}`);
} else {
console.log(`SMS to ${msg.phone}`);
}
}FAQ
Use a type alias when modeling unions (string | number), tuples, function signatures, or primitive aliases. Use interface when defining object shapes meant to be implemented by classes or extended across package boundaries.
While type aliases do not support the extends keyword directly, you achieve the same inheritance outcome by combining shapes with the intersection & operator. Interfaces can also extend object-shaped type aliases seamlessly.
type Base = { id: string };
type Item = Base & { name: string };A union (A | B) represents an OR relationship where a value satisfies either contract A or contract B. An intersection (A & B) represents an AND relationship where a value must simultaneously fulfill all requirements of both shapes.
A discriminated union is a set of object types sharing a common literal property, like kind: 'success' | 'error'. TypeScript inspects this discriminant key inside switch or if branches to automatically narrow the object type.