Julio Rodriguez
← Playground

TypeScript Patterns

A quick tour of the TypeScript features used throughout this codebase.

Types vs. Interfaces

Both describe the shape of an object. interface can be extended and merged; type can describe unions, tuples, and other shapes an interface can't.

type UserRole = "admin" | "editor" | "viewer";
 
interface User {
  id: string;
  name: string;
  role: UserRole;
}
 
// Interfaces can be extended...
interface AdminUser extends User {
  permissions: string[];
}

Generics

Generics let a function or type work with the caller's type instead of a fixed one, while staying type-safe.

function firstItem<T>(items: T[]): T | undefined {
  return items[0];
}
 
const firstUser = firstItem<User>(users); // User | undefined
const firstNumber = firstItem([1, 2, 3]); // number | undefined (inferred)

Utility Types

TypeScript ships a handful of built-in types for transforming existing ones.

type PartialUser = Partial<User>; // every field optional
type UserPreview = Pick<User, "id" | "name">; // only id and name
type UserWithoutRole = Omit<User, "role">; // everything except role
type UsersById = Record<string, User>; // { [id: string]: User }

Inference

TypeScript infers types from usage, so you often don't need to annotate everything by hand.

const roles = ["admin", "editor", "viewer"] as const;
// type: readonly ["admin", "editor", "viewer"]
 
function greet(user: User) {
  return `Hi, ${user.name}`; // return type inferred as string
}