Generics, Tuples, and Enums
Unions and intersections combine types you already have. TypeScript also has some more specialized type features. Generics let you write code that works with any type, tuples give you fixed-shape arrays, and enums define sets of named constants.
Generics
Generics let you write reusable components that work with any type while maintaining type safety. A generic uses a type parameter (conventionally T) as a placeholder:
function identity<T>(value: T): T {
return value;
}
identity<string>("hello"); // "hello" — T is string
identity<number>(42); // 42 — T is number
identity("hello"); // "hello" — TypeScript infers T as string
Generics are especially useful with data structures:
class Box<T> {
constructor(public contents: T) {}
getContents(): T {
return this.contents;
}
}
const stringBox = new Box("hello");
console.log(stringBox.getContents()); // "hello"
const numberBox = new Box(42);
console.log(numberBox.getContents()); // 42
You can constrain generics to require certain properties:
function getLength<T extends { length: number }>(item: T): number {
return item.length;
}
getLength("hello"); // 5
getLength([1, 2, 3]); // 3
getLength(42); // Error: Number doesn't have a 'length' property
Tuples
A tuple is an array with a fixed number of elements where each element has a known type:
let entry: [string, number] = ["Ali", 30];
console.log(entry[0]); // "Ali"
console.log(entry[1]); // 30
Unlike regular arrays, tuples enforce both the length and the type at each position:
entry = [30, "Ali"]; // Error: types are in wrong order
entry = ["Ali"]; // Error: missing second element
Tuples are handy for functions that return multiple values:
function parseCoordinate(input: string): [number, number] {
const [x, y] = input.split(",").map(Number);
return [x, y];
}
const [lat, lng] = parseCoordinate("40.7,-74.0");
console.log(lat); // 40.7
console.log(lng); // -74
Optional and Rest Elements in Tuples
Tuples can have optional elements (?) and rest elements (...):
type NameAndAge = [string, number?];
const withAge: NameAndAge = ["Ali", 30]; // OK
const withoutAge: NameAndAge = ["Ali"]; // OK
type AtLeastOne = [number, ...number[]];
const nums: AtLeastOne = [1, 2, 3, 4]; // OK
Enums
An enum defines a set of named constants. Use the enum keyword:
enum Direction {
Up,
Down,
Left,
Right,
}
const dir: Direction = Direction.Up;
console.log(dir); // 0
By default, enum members get numeric values starting from 0. You can assign custom values:
enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE",
Pending = "PENDING",
}
const userStatus: Status = Status.Active;
console.log(userStatus); // "ACTIVE"
String enums are generally preferred because their values are meaningful and readable at runtime.
Utility Types
TypeScript provides several built-in utility types that transform existing types. Here are the most useful ones:
interface User {
name: string;
email: string;
age: number;
}
// All properties optional
type PartialUser = Partial<User>;
// { name?: string; email?: string; age?: number }
// All properties required
type RequiredUser = Required<PartialUser>;
// { name: string; email: string; age: number }
// Pick specific properties
type UserPreview = Pick<User, "name" | "email">;
// { name: string; email: string }
// Exclude specific properties
type UserWithoutAge = Omit<User, "age">;
// { name: string; email: string }
Other common utility types include Record<K, V> for defining key-value types, Readonly<T> for making all properties read-only, and ReturnType<T> for extracting the return type of a function.