Skip to content
JL

Home

About

Blog

Contact

Shop

Portfolio

Privacy

TOS

Click to navigate

  1. Home
  2. Joshua R. Lehman's Blog
  3. Utility Types Part 1: Partial and Required

Table of contents

  • Share on X
  • Discuss on X

Related Articles

Utility Types Part 3: Pick and Omit
TypeScript
9m
Aug 30, 2026

Utility Types Part 3: Pick and Omit

Rather than transforming every property of a type — as Partial, Required, and Readonly do — Pick and Omit restructure a type by including or excluding specific named properties. Pick produces a type containing only the properties you name. Omit produces a type containing every property except the ones you name.

#Utility Types#Pick+5
Utility Types Part 2: Readonly and Record
TypeScript
10m
Aug 23, 2026

Utility Types Part 2: Readonly and Record

Two utility types that occupy opposite ends of the structural transformation space: Readonly locks an object's properties against mutation at the type level, while Record builds typed dictionary structures from a set of keys and a value type. Neither modifies optionality — they operate on shape and mutability instead.

#Utility Types#Readonly+5
Mastering TypeScript Utility Types
TypeScript
9m
Feb 1, 2026

Mastering TypeScript Utility Types

TypeScript provides powerful built-in utility types that help you transform existing types without rewriting them. This guide covers the most essential utility types and how to use them effectively in your projects.

#TypeScript#Utility Types+4
Ask me anything! 💬

© Joshua R. Lehman

Full Stack Developer

Crafted with passion • Built with modern web technologies

2026 • All rights reserved

Contents

  • What Utility Types Are
  • Partial
  • How Partial Is Implemented
  • Required
  • How Required Is Implemented
  • Practical Patterns for Partial
  • Practical Patterns for Required
  • Deep Partial and Its Limits
  • What Is Next
  • Key Takeaways
TypeScript

Utility Types Part 1: Partial and Required

August 16, 2026•8 min read
Joshua R. Lehman
Joshua R. Lehman
Author
TypeScript Partial and Required utility types transforming object property optionality
Utility Types Part 1: Partial and Required

Object types in TypeScript specify which properties exist, what types they hold, and whether each is optional or required. In most cases these details are set once in the type definition and stay fixed. But many operations on objects naturally require a different optionality profile than the base type: an update operation that only changes some properties, a configuration object where every field has a default, a form state that starts empty before the user fills it in.

Writing a separate type for each of these scenarios — manually prefixing every property with ? or removing ? from every property — is tedious and fragile. Change the base type and you must update every derived variant by hand. TypeScript's utility types solve this with a single generic: Partial<T> produces a version of T where every property is optional, and Required<T> produces a version where every property is required. Both are built into the TypeScript standard library and require no imports.

What Utility Types Are

Utility types are generic type aliases defined in TypeScript's lib.es5.d.ts declaration file. They accept one or more type parameters and produce a new type by applying a transformation. You use them the same way you use any generic type — by passing a type argument in angle brackets:

type User = {
  id: number;
  name: string;
  email: string;
  role: "admin" | "user";
};
 
type PartialUser = Partial<User>;
// Equivalent to:
// {
//   id?: number;
//   name?: string;
//   email?: string;
//   role?: "admin" | "user";
// }

All utility types are pure type-level constructs. They exist only at compile time, produce no runtime code, and have zero performance cost at runtime. They are syntactic shorthand for mapped type patterns that you could write yourself — but because they are standardised and named, they communicate intent to anyone reading the code.

Utility Types Are Not Magic

Every utility type in TypeScript's standard library is implemented using mapped types, conditional types, and the infer keyword — the same tools you have access to. Looking at how they are defined (navigate to their definitions in your editor with "Go to Definition") is one of the best ways to understand advanced TypeScript type patterns. Partial, Required, Readonly, Pick, Omit, and the rest are each a few lines of type-level code.

Partial

Partial<T> takes an object type T and returns a new type where every property of T is optional. Properties that were already optional remain optional. Properties that were required become optional.

type User = {
  id: number;
  name: string;
  email: string;
};
 
type PartialUser = Partial<User>;
// {
//   id?: number | undefined;
//   name?: string | undefined;
//   email?: string | undefined;
// }

The resulting type PartialUser can be constructed with any subset of properties — including none at all:

const empty: PartialUser = {};
const nameOnly: PartialUser = { name: "Alice" };
const full: PartialUser = { id: 1, name: "Alice", email: "[email protected]" };

All three are valid. Partial<T> does not validate that any properties are present — it only ensures that any properties that are present match their declared types.

How Partial Is Implemented

Partial<T> is defined in the TypeScript standard library as:

type Partial<T> = {
  [P in keyof T]?: T[P];
};

This is a mapped type — it iterates over every key P in keyof T (the union of all property keys of T) and produces a new property with the ? modifier added. The property type T[P] is the indexed access type for that key — the original value type, unchanged.

Understanding this implementation reveals what Partial does and does not do:

  • It applies ? to every property, including nested object properties
  • It does not recursively apply to nested objects — a nested object property becomes optional, but the object itself is not made partial
  • The value types of each property are preserved exactly as in T

This last point is important: Partial<User> makes User.id optional, but the type of id when present is still number — not number | undefined. The ? modifier adds undefined to the assignable types but does not change the declared value type.

Required

Required<T> is the complement of Partial. It takes a type T and returns a version where every optional property has its ? modifier removed — making all properties required.

type Config = {
  timeout?: number;
  retries?: number;
  baseUrl?: string;
  debug?: boolean;
};
 
type RequiredConfig = Required<Config>;
// {
//   timeout: number;
//   retries: number;
//   baseUrl: string;
//   debug: boolean;
// }

RequiredConfig requires all four properties. A value of type RequiredConfig cannot omit any of them:

// Error: Property 'retries' is missing
const cfg: RequiredConfig = {
  timeout: 5000,
  baseUrl: "https://api.example.com",
  debug: false,
};

Required is most useful when you have a configuration type with many optional fields (because they have defaults), but at some point in the application — after applying defaults — you need to work with a fully resolved configuration where every value is guaranteed to be present.

How Required Is Implemented

Required<T> is defined as:

type Required<T> = {
  [P in keyof T]-?: T[P];
};

The -? syntax is the mapped type modifier removal operator. +? adds the optional modifier; -? removes it. Required uses -? to strip the ? from every property key in the mapped type. Properties that were already required are unaffected.

Required Does Not Remove undefined from Value Types

Required<T> removes the optional modifier from properties, but it does not remove undefined from explicit union types. If your type has value: string | undefined (not value?: string), Required will not remove the undefined. The distinction matters: { value?: string } is equivalent to { value?: string | undefined } — the ? makes undefined implicit. But { value: string | undefined } explicitly includes undefined in the value type and is unaffected by Required. Only the ? modifier is stripped, not explicit | undefined in the declared type.

Practical Patterns for Partial

Update DTOs (Data Transfer Objects). When updating a resource via an API, clients typically send only the properties they want to change. Partial<T> is the natural type for the update payload:

type Post = {
  id: number;
  title: string;
  content: string;
  publishedAt: Date;
  authorId: number;
};
 
type PostUpdate = Partial<Omit<Post, "id" | "authorId">>;
// {
//   title?: string;
//   content?: string;
//   publishedAt?: Date;
// }
 
function updatePost(id: number, changes: PostUpdate): Promise<Post> {
  // Apply only the provided changes to the stored post
  return applyChanges(id, changes);
}

Combining Partial with Omit (covered in the next post) is a common pattern for update operations — exclude the immutable fields (id, authorId) and make the rest optional.

Form state. A form starts with no values filled in and accumulates them as the user types. Partial<T> is the natural type for in-progress form state:

type RegistrationForm = {
  username: string;
  email: string;
  password: string;
  confirmPassword: string;
};
 
type FormState = Partial<RegistrationForm>;
 
const [formState, setFormState] = useState<FormState>({});
 
function updateField<K extends keyof FormState>(key: K, value: FormState[K]) {
  setFormState((prev) => ({ ...prev, [key]: value }));
}

Builder patterns. Builder pattern implementations often accumulate properties step by step. Partial<T> represents the in-progress state:

class QueryBuilder {
  private config: Partial<QueryConfig> = {};
 
  select(fields: string[]): this {
    this.config.fields = fields;
    return this;
  }
 
  where(condition: string): this {
    this.config.condition = condition;
    return this;
  }
 
  build(): QueryConfig {
    if (!this.config.fields) throw new Error("fields required");
    return this.config as QueryConfig;
  }
}

Use Partial for Optional Configuration Parameters

Functions that accept configuration objects benefit from Partial<T> combined with a defaults merge. Define the full config type with all fields required, accept Partial<Config> as the parameter, and merge with defaults at the start of the function. This pattern gives callers maximum flexibility while the internal implementation works with a fully resolved config: const config: Config = { ...defaultConfig, ...userConfig }.

Practical Patterns for Required

Post-validation states. After validating or deserialising data, you often want to assert that all fields are present. Required<T> communicates that a previously-partial value has been fully populated:

function validateConfig(raw: Partial<Config>): Required<Config> {
  if (!raw.timeout) throw new Error("timeout required");
  if (!raw.baseUrl) throw new Error("baseUrl required");
  if (!raw.retries) throw new Error("retries required");
  // After validation, we know all fields are present
  return raw as Required<Config>;
}

Ensuring complete object construction. When constructing objects through a builder or accumulation pattern, Required can be used at the end of the chain to verify completeness:

function buildReport(parts: Partial<Report>): Required<Report> {
  const complete = applyDefaults(parts);
  assertComplete(complete); // throws if any field is missing
  return complete;
}

Deep Partial and Its Limits

Partial<T> only makes top-level properties optional. Nested object properties become optional (because the entire nested object becomes optional), but the properties of those nested objects remain as declared in the original type.

type Settings = {
  display: {
    theme: string;
    fontSize: number;
  };
  notifications: {
    email: boolean;
    push: boolean;
  };
};
 
type PartialSettings = Partial<Settings>;
// {
//   display?: {
//     theme: string;   // Still required!
//     fontSize: number; // Still required!
//   };
//   notifications?: {
//     email: boolean;  // Still required!
//     push: boolean;   // Still required!
//   };
// }

display and notifications are optional, but if either is provided, it must be provided in full. To make every property at every nesting level optional, you need a recursive DeepPartial type:

type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

DeepPartial is not a built-in utility type — you define it yourself (or use a library). Whether you need shallow or deep partiality depends on your use case. Update DTOs typically benefit from deep partiality; form state management often works fine with shallow.

What Is Next

Partial and Required are the first two of TypeScript's built-in utility types. The next post covers Readonly and Record — two more utility types from the standard library. Readonly produces an immutable version of an object type; Record constructs a dictionary type from a set of keys and a value type.

Key Takeaways

  • Partial<T> adds ? to every property in T, making them all optional; it is implemented as { [P in keyof T]?: T[P] }
  • Required<T> removes ? from every property in T, making them all required; it is implemented as { [P in keyof T]-?: T[P] }
  • Partial is useful for update DTOs, form state, and configuration with defaults; Required is useful for post-validation states and asserting complete construction
  • Both utility types are shallow — they do not recursively transform nested object types; for deep transformation, write a recursive DeepPartial or DeepRequired custom utility type
  • Required removes the optional modifier but does not remove explicit | undefined from value types — the two are distinct in TypeScript's type system