TS

Utility Types

TypeScript · 10 entries

Partial<T>

syntax
type Result = Partial<OriginalType>;
example
interface Settings {
  theme: "light" | "dark";
  fontSize: number;
  notifications: boolean;
}

function updateSettings(current: Settings, changes: Partial<Settings>): Settings {
  return { ...current, ...changes };
}

const updated = updateSettings(
  { theme: "light", fontSize: 14, notifications: true },
  { fontSize: 16 } // only need to pass what changed
);
output
// Partial<Settings> makes all properties optional

Note Partial only operates one level deep. Nested objects keep their original required types. For deep partial, you need a custom recursive type or a library utility.

Required<T>

syntax
type Result = Required<OriginalType>;
example
interface FormFields {
  username?: string;
  email?: string;
  password?: string;
}

// After validation, all fields must be present
type ValidatedForm = Required<FormFields>;

const validated: ValidatedForm = {
  username: "alice",
  email: "alice@example.com",
  password: "s3cureP@ss",
};
// Missing any field → compile error
output
// Required<T> removes all ? optional markers

Note Required is the inverse of Partial. Like Partial, it only works one level deep. Useful for representing the 'after-validation' state where you know all optional fields have been filled in.

Readonly<T>

syntax
type Result = Readonly<OriginalType>;
example
interface AppState {
  user: { name: string; role: string };
  items: string[];
  isLoading: boolean;
}

function freezeState(state: AppState): Readonly<AppState> {
  return Object.freeze(state);
}

const frozen = freezeState({ user: { name: "A", role: "admin" }, items: [], isLoading: false });
// frozen.isLoading = true; // Error: Cannot assign to 'isLoading'
output
// Readonly<T> marks all top-level properties as readonly

Note Readonly only applies to the top level. frozen.user.name = 'B' would still compile because the nested object is not frozen. For deep immutability, use 'as const' on literals, or build a DeepReadonly recursive type.

Pick<T, Keys>

syntax
type Result = Pick<OriginalType, "key1" | "key2">;
example
interface Employee {
  id: string;
  name: string;
  email: string;
  department: string;
  salary: number;
}

type EmployeePreview = Pick<Employee, "id" | "name" | "department">;

const preview: EmployeePreview = {
  id: "emp_042",
  name: "Jordan",
  department: "Engineering",
};
output
// EmployeePreview has only id, name, and department

Note Pick creates a new type with only the specified keys. The keys must actually exist on T — typos cause compile errors. Pick is great for API response shapes where you only need a subset of fields.

Omit<T, Keys>

syntax
type Result = Omit<OriginalType, "key1" | "key2">;
example
interface BlogPost {
  id: string;
  title: string;
  body: string;
  authorId: string;
  createdAt: Date;
}

type CreatePostInput = Omit<BlogPost, "id" | "createdAt">;

const newPost: CreatePostInput = {
  title: "Getting Started with TypeScript",
  body: "TypeScript adds type safety to JavaScript...",
  authorId: "usr_007",
};
output
// CreatePostInput has title, body, authorId (no id or createdAt)

Note Omit does NOT enforce that the keys exist on T — you can omit keys that are not present without error. This is a known gotcha; misspelled keys silently pass. Use a custom StrictOmit if you need safety.

Record<Keys, Value>

syntax
type Result = Record<KeyType, ValueType>;
example
type Role = "admin" | "editor" | "viewer";

interface Permission {
  canRead: boolean;
  canWrite: boolean;
  canDelete: boolean;
}

const rolePermissions: Record<Role, Permission> = {
  admin:  { canRead: true,  canWrite: true,  canDelete: true },
  editor: { canRead: true,  canWrite: true,  canDelete: false },
  viewer: { canRead: true,  canWrite: false, canDelete: false },
};
output
// Record forces every Role to have a Permission entry

Note When Keys is a union of string literals, Record ensures every literal is present — great for exhaustive mappings. Record<string, T> is equivalent to { [key: string]: T } and does not enforce specific keys.

Exclude & Extract

syntax
type Result = Exclude<UnionType, ExcludedMembers>;
type Result = Extract<UnionType, ExtractedMembers>;
example
type AllEvents = "click" | "scroll" | "keydown" | "keyup" | "resize";

type KeyboardEvents = Extract<AllEvents, "keydown" | "keyup">;
// "keydown" | "keyup"

type NonKeyboardEvents = Exclude<AllEvents, "keydown" | "keyup">;
// "click" | "scroll" | "resize"

// Works with complex types too
type OnlyStrings = Extract<string | number | boolean, string>;
// string
output
// Exclude removes members; Extract keeps matching members

Note These work on union members, not object properties. To remove properties from an object, use Omit. Exclude and Extract filter which union members match the condition using distributive conditional types under the hood.

NonNullable<T>

syntax
type Result = NonNullable<Type>;
example
type MaybeUser = string | null | undefined;
type DefiniteUser = NonNullable<MaybeUser>;
// string

function processValue(input: string | null | undefined) {
  // After validation
  const safe: NonNullable<typeof input> = input!;
  console.log(safe.toUpperCase());
}
output
// NonNullable removes null and undefined from a union

Note NonNullable is shorthand for Exclude<T, null | undefined>. It is commonly used after runtime null checks to assert the cleaned-up type. Pairs well with strictNullChecks.

ReturnType & Parameters

syntax
type Ret = ReturnType<typeof fn>;
type Params = Parameters<typeof fn>;
example
function createOrder(userId: string, items: string[], coupon?: string) {
  return {
    orderId: `ord_${Date.now()}`,
    userId,
    items,
    discount: coupon ? 0.1 : 0,
  };
}

type OrderResult = ReturnType<typeof createOrder>;
// { orderId: string; userId: string; items: string[]; discount: number }

type OrderParams = Parameters<typeof createOrder>;
// [userId: string, items: string[], coupon?: string]
output
// ReturnType extracts what the function returns; Parameters extracts the argument tuple

Note Both require typeof when used with a concrete function (not a type). For class constructors, use ConstructorParameters<typeof ClassName>. ReturnType is invaluable for inferring types from existing functions without duplicating definitions.

Awaited<T>

syntax
type Result = Awaited<PromiseType>;
example
type PromisedUser = Promise<{ id: string; name: string }>;
type ResolvedUser = Awaited<PromisedUser>;
// { id: string; name: string }

// Handles nested promises
type DeepPromise = Promise<Promise<Promise<number>>>;
type DeepResolved = Awaited<DeepPromise>;
// number

// Practical: extract resolved type from async function
async function loadConfig() {
  return { apiUrl: "https://api.example.com", timeout: 5000 };
}
type Config = Awaited<ReturnType<typeof loadConfig>>;
output
// Awaited recursively unwraps all layers of Promise

Note Added in TS 4.5. Awaited recursively unwraps nested Promises until it hits a non-Promise type. Before Awaited existed, developers had to write custom recursive unwrap types. Works with any thenable, not just native Promises.