Basic Types
TypeScript adds static types to JavaScript. This catches errors at compile time rather than runtime, making your code more reliable.
// Primitive types
const name: string = "Alice";
const age: number = 25;
const isActive: boolean = true;
const value: null = null;
const nothing: undefined = undefined;
const bigNum: bigint = 9007199254740991n;
// Arrays
const numbers: number[] = [1, 2, 3];
const names: Array<string> = ["Alice", "Bob"];
// Tuples - fixed length arrays with specific types
const point: [number, number] = [10, 20];
const entry: [string, number] = ["Alice", 25];
// Object types inline
const user: { name: string; age: number; email?: string } = {
name: "Alice",
age: 25,
};
// Union types - either/or
type ID = string | number;
let userId: ID = "user-123";
userId = 456; // Also valid
// Literal types
type Direction = "north" | "south" | "east" | "west";
type StatusCode = 200 | 201 | 400 | 404 | 500;
const dir: Direction = "north";
// const invalid: Direction = "up"; // Error!
// Type assertions
const input = document.getElementById("myInput") as HTMLInputElement;
Union & Intersection Types
Union types represent values that can be one of several types. Intersection types combine multiple types into one.
// Union type: value is one OR the other
type StringOrNumber = string | number;
type NullableString = string | null | undefined;
// Discriminated unions - powerful pattern for exhaustive checks
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rectangle'; width: number; height: number }
| { kind: 'triangle'; base: number; height: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'rectangle':
return shape.width * shape.height;
case 'triangle':
return (shape.base * shape.height) / 2;
// TypeScript knows all cases are handled
}
}
// Intersection type: value has ALL properties
type Serializable = { serialize(): string };
type Loggable = { log(msg: string): void };
type SerializableAndLoggable = Serializable & Loggable;
// Combining object types
type BaseUser = { id: string; email: string };
type AdminUser = BaseUser & {
role: 'admin';
permissions: string[];
};
const admin: AdminUser = {
id: "1",
email: "admin@example.com",
role: "admin",
permissions: ["read", "write", "delete"],
};
Utility Types
TypeScript provides built-in utility types that transform existing types. These are incredibly useful for building type-safe applications.
interface User {
id: string;
name: string;
email: string;
password: string;
createdAt: Date;
updatedAt: Date;
}
// Partial - all properties optional
type UserUpdate = Partial<User>;
// { id?: string; name?: string; email?: string; ... }
// Required - all properties required
type RequiredUser = Required<UserUpdate>;
// Pick - select subset of properties
type UserPublic = Pick<User, 'id' | 'name' | 'email'>;
// { id: string; name: string; email: string }
// Omit - exclude specific properties
type UserWithoutPassword = Omit<User, 'password'>;
// Readonly - all properties read-only
type ImmutableUser = Readonly<User>;
// Record - key/value map
type UserRoles = Record<string, 'admin' | 'editor' | 'viewer'>;
const roles: UserRoles = {
alice: 'admin',
bob: 'editor',
};
// ReturnType & Parameters
async function fetchUser(id: string): Promise<User> { /* ... */ return {} as User; }
type FetchUserReturn = Awaited<ReturnType<typeof fetchUser>>; // User
type FetchUserParams = Parameters<typeof fetchUser>; // [string]
// Extract & Exclude
type OnlyString = Extract<string | number | boolean, string>; // string
type NotString = Exclude<string | number | boolean, string>; // number | boolean
Utility types are your best friend for DRY type definitions. Instead of duplicating interfaces, derive them from existing ones.