React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

typescriptreactPartialPickOmitRecordExtractutility-types

Utility Types for React

Recipe

Use TypeScript's built-in utility types to transform and compose React prop types. Derive new types from existing ones instead of duplicating definitions.

Working Example

// Base type
type User = {
  id: string;
  name: string;
  email: string;
  role: "admin" | "editor" | "viewer";
  createdAt: Date;
};
 
// Partial - all fields become optional (great for update forms)
type UpdateUserPayload = Partial<User>;
 
// Pick - select specific fields
type UserPreview = Pick<User, "id" | "name">;
 
// Omit - exclude specific fields
type CreateUserPayload = Omit<User, "id" | "createdAt">;
 
// Record - typed key-value map
type RolePermissions = Record<User["role"], string[]>;
 
const permissions: RolePermissions = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
};
// Practical component using derived types
type UserFormProps = {
  initialData?: Partial<User>;
  onSubmit: (data: CreateUserPayload) => void;
};
 
function UserForm({ initialData, onSubmit }: UserFormProps) {
  const [form, setForm] = useState<CreateUserPayload>({
    name: initialData?.name ?? "",
    email: initialData?.email ?? "",
    role: initialData?.role ?? "viewer",
  });
 
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        onSubmit(form);
      }}
    >
      <input
        value={form.name}
        onChange={(e) => setForm({ ...form, name: e.target.value })}
      />
      <input
        value={form.email}
        onChange={(e) => setForm({ ...form, email: e.target.value })}
      />
      <button type="submit">Save</button>
    </form>
  );
}

Deep Dive

How It Works

  • Partial<T> makes every property in T optional. Useful for update payloads where you only change some fields.
  • Required<T> is the inverse of Partial -- makes every property required. Useful for normalizing config objects with defaults.
  • Pick<T, K> creates a type with only the specified keys from T. Use it to create focused sub-types.
  • Omit<T, K> creates a type with all keys except the specified ones. Common for create payloads where the server generates id.
  • Record<K, V> creates an object type with keys of type K and values of type V. Perfect for lookup maps.
  • Extract<T, U> pulls members from a union that match U. Extract<User["role"], "admin" | "editor"> yields "admin" | "editor".
  • Exclude<T, U> removes members from a union that match U. The inverse of Extract.

Variations

Extracting component prop types:

// Get props from an existing component
type ButtonProps = React.ComponentProps<typeof Button>;
 
// Get props of an HTML element
type DivProps = React.ComponentPropsWithoutRef<"div">;
 
// Extend HTML element props
type CardProps = React.ComponentPropsWithoutRef<"div"> & {
  title: string;
  elevated?: boolean;
};

Readonly props:

type ImmutableUser = Readonly<User>;
// All fields are now readonly - prevents accidental mutation
 
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

Mapped types for form state:

type FormErrors<T> = Partial<Record<keyof T, string>>;
 
type UserFormErrors = FormErrors<CreateUserPayload>;
// { name?: string; email?: string; role?: string }

NonNullable for stripping null/undefined:

type MaybeUser = User | null | undefined;
type DefiniteUser = NonNullable<MaybeUser>; // User

TypeScript Notes

  • Utility types compose: Partial<Pick<User, "name" | "email">> gives you { name?: string; email?: string }.
  • React.ComponentProps<typeof MyComponent> extracts the props type from any component, even third-party ones where the type is not exported.
  • Parameters<T> and ReturnType<T> work on function types. ReturnType<typeof useAuth> gives you the return type of a custom hook.

Gotchas

  • Omit does not error on keys that do not exist in the original type. Omit<User, "nonExistent"> silently returns the full User type.
  • Partial makes everything optional, including fields that should remain required. Use Partial<Pick<T, K>> & Omit<T, K> to make only some fields optional.
  • Record<string, T> allows any string key. This can hide typos. Prefer union literal keys when the set of keys is known.
  • Deeply nested objects are not affected by Partial or Readonly. These only operate on the top-level properties. You need custom recursive types for deep transformations.

Alternatives

ApproachProsCons
Partial<T> for updatesDerived from source type, stays in syncMakes all fields optional, not just some
Manual sub-typesFull control over each typeDuplicates definitions, drifts from source
Pick / Omit compositionPrecise field selectionDeeply nested compositions are hard to read
Zod .pick() / .omit()Runtime validation + type inferenceRequires Zod dependency
satisfies operatorValidates shape without wideningDoes not create a reusable type

FAQs

What does Partial<T> do and when should I use it?
  • It makes every property in T optional.
  • Use it for update payloads where you only change some fields, e.g., Partial<User> for a PATCH request body.
What is the difference between Pick and Omit?
  • Pick<T, K> creates a type with only the specified keys from T.
  • Omit<T, K> creates a type with all keys except the specified ones.
  • Use Pick when you want a small subset; use Omit when you want most fields minus a few.
How do I extract the props type from an existing component?
type ButtonProps = React.ComponentProps<typeof Button>;
type DivProps = React.ComponentPropsWithoutRef<"div">;
  • Works on both custom components and HTML elements.
What does Record<K, V> do?
  • It creates an object type with keys of type K and values of type V.
  • Example: Record<User["role"], string[]> maps each role to an array of permission strings.
What is the difference between Extract and Exclude?
  • Extract<T, U> keeps union members that match U.
  • Exclude<T, U> removes union members that match U.
  • Example: Extract<"admin" | "editor" | "viewer", "admin" | "editor"> yields "admin" | "editor".
Can I compose utility types together?
  • Yes. They compose freely: Partial<Pick<User, "name" | "email">> gives { name?: string; email?: string }.
  • Composition keeps types derived from a single source of truth.
Gotcha: Does Omit error if I specify a key that does not exist on the type?
  • No. Omit<User, "nonExistent"> silently returns the full User type.
  • TypeScript does not warn you, so double-check your key names.
Gotcha: Does Partial work on deeply nested objects?
  • No. Partial only affects top-level properties.
  • For deep optionality, you need a custom recursive type like DeepPartial<T>.
How do I make only some fields optional while keeping others required?
type UpdateUser = Partial<Pick<User, "name" | "email">> & Omit<User, "name" | "email">;
  • Combine Partial<Pick<>> for the optional fields with Omit<> for the rest.
What is NonNullable<T> used for?
  • It strips null and undefined from a type.
  • NonNullable<User | null | undefined> gives User.
How do I get the return type of a custom hook?
type AuthData = ReturnType<typeof useAuth>;
  • ReturnType<T> works on any function type, including hooks and utility functions.
What is a mapped type and how does FormErrors use it?
type FormErrors<T> = Partial<Record<keyof T, string>>;
  • Record<keyof T, string> maps every key of T to a string value.
  • Wrapping in Partial makes each error message optional.