TSTypeScript · Lesson 7 of 10

Utility Types

TypeScript ships a library of generic utility types that transform existing types. These replace entire categories of repetitive type definitions. Once you know them, you'll wonder how you lived without them.

TypeScript
interface User {
  id: number
  name: string
  email: string
  password: string
  createdAt: Date
  role: "user" | "admin"
}

// Partial<T> — all properties become optional
type UserUpdate = Partial<User>
// { id?: number; name?: string; email?: string; ... }

// Required<T> — all optional properties become required
type StrictUser = Required<UserUpdate>

// Readonly<T> — all properties become readonly
type FrozenUser = Readonly<User>

// Pick<T, Keys> — select a subset of properties
type UserPublic = Pick<User, "id" | "name" | "role">
// { id: number; name: string; role: "user" | "admin" }

// Omit<T, Keys> — exclude properties
type UserWithoutPassword = Omit<User, "password">

// Record<Keys, Type> — create an object type with specific keys
type RolePermissions = Record<"user" | "admin", string[]>
const perms: RolePermissions = {
  user:  ["read"],
  admin: ["read", "write", "delete"],
}

// Exclude<T, U> — remove members from union
type NotAdmin = Exclude<"user" | "admin" | "guest", "admin">
// "user" | "guest"

// Extract<T, U> — keep only members assignable to U
type StringOrNumber = Extract<string | number | boolean, string | number>
// string | number
TypeScript
// ReturnType<T> — extract return type of a function
function getUser() {
  return { id: 1, name: "Alice", email: "alice@example.com" }
}
type UserFromFn = ReturnType<typeof getUser>
// { id: number; name: string; email: string }

// Parameters<T> — extract parameter types as tuple
function createPost(title: string, body: string, authorId: number) {}
type CreatePostArgs = Parameters<typeof createPost>
// [string, string, number]

// Awaited<T> — unwrap Promise type
type ResolvedUser = Awaited<Promise<User>>   // User

// NonNullable<T> — remove null and undefined
type DefinitelyString = NonNullable<string | null | undefined>
// string

// Practical example — form handling pattern
type FormValues = Omit<User, "id" | "createdAt">
type FormErrors = Partial<Record<keyof FormValues, string>>

function validateUser(values: FormValues): FormErrors {
  const errors: FormErrors = {}
  if (!values.name)  errors.name  = "Name is required"
  if (!values.email.includes("@")) errors.email = "Invalid email"
  if (values.password.length < 8)  errors.password = "Min 8 characters"
  return errors
}
✦ Tip
Before defining a new interface, ask if a utility type can derive it from an existing one. `Omit<User, "password">` is better than a separate `PublicUser` interface that you have to keep in sync. If the source type changes, derived types update automatically.