TypeScript by Example

Utility Types

The TypeScript stdlib ships 20+ built-in utility types - Partial, Readonly, Pick, Omit, Exclude, ReturnType, and Awaited cover most day-to-day needs.

TypeScript's standard library ships a set of utility types built from mapped types, conditional types, and infer. You don't need to install anything - they're available in every TypeScript project.

Partial<T> makes every property optional. Required<T> does the reverse. Readonly<T> prevents mutation. Record<K, V> constructs an object type with keys K and values V - useful for lookup tables and index signatures.

type User = { id: string; name: string; email: string };
 
// Partial - all fields optional (common for update payloads)
type UserPatch = Partial<User>;
// { id?: string; name?: string; email?: string }
 
// Required - all fields mandatory (strips optional markers)
type FullUser = Required<UserPatch>;
// { id: string; name: string; email: string }
 
// Readonly - no assignments after construction
type ImmutableUser = Readonly<User>;
const u: ImmutableUser = { id: "1", name: "Ada", email: "ada@example.com" };
// u.name = "Grace"; // Error: cannot assign to 'name' - it is read-only
 
// Record - maps a union of keys to a value type
type RolePermissions = Record<"admin" | "editor" | "viewer", string[]>;
const permissions: RolePermissions = {
  admin:  ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
};

Pick<T, K> keeps only the listed keys. Omit<T, K> drops them. Exclude<T, U> removes members of a union that extend U. Extract<T, U> keeps only members that extend U.

type Article = {
  id: string;
  title: string;
  body: string;
  authorId: string;
  publishedAt: Date | null;
};
 
// Pick - keep only what the list endpoint needs
type ArticleSummary = Pick<Article, "id" | "title" | "publishedAt">;
// { id: string; title: string; publishedAt: Date | null }
 
// Omit - strip internal fields before sending to the client
type PublicArticle = Omit<Article, "authorId">;
// { id: string; title: string; body: string; publishedAt: Date | null }
 
type Status = "pending" | "active" | "suspended" | "deleted";
 
// Exclude - remove specific members from a union
type LiveStatus = Exclude<Status, "deleted" | "suspended">;
// "pending" | "active"
 
// Extract - keep only the matching members
type TerminalStatus = Extract<Status, "suspended" | "deleted">;
// "suspended" | "deleted"

ReturnType<F> extracts the return type of a function. Parameters<F> extracts the parameter types as a tuple. InstanceType<C> extracts the instance type of a class constructor. Awaited<T> recursively unwraps Promise wrappers.

function fetchUser(id: string): Promise<{ id: string; name: string }> {
  return Promise.resolve({ id, name: "Ada" });
}
 
// ReturnType - derive the return type without repeating it
type FetchUserReturn = ReturnType<typeof fetchUser>;
// Promise<{ id: string; name: string }>
 
// Awaited - unwrap the Promise
type ResolvedUser = Awaited<FetchUserReturn>;
// { id: string; name: string }
 
// Parameters - extract argument types as a tuple
type FetchUserParams = Parameters<typeof fetchUser>;
// [id: string]
 
class Repository {
  constructor(private db: string) {}
  find(id: string): Promise<{ id: string }> {
    return Promise.resolve({ id });
  }
}
 
// InstanceType - get the type of an instance from its constructor
type RepoInstance = InstanceType<typeof Repository>;
// Repository

In production

ReturnType<typeof fn> is especially useful for third-party code you can't modify - derive the type from its runtime behavior instead of maintaining a duplicate annotation that will drift. Awaited<T> is essential for async pipelines: Awaited<ReturnType<typeof fetchUser>> gives you the resolved payload type without additional ceremony. For update payloads, reach for Partial<T> at the API boundary and Required<T> at the persistence boundary - this makes the required/optional contract explicit at every layer. Avoid Partial in function bodies where the data should already be complete; a missing field should be a type error, not a runtime surprise.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free