TypeScriptPartial & Required

Partial & Required

Two of the most commonly used utility types in TypeScript are Partial<T> and Required<T>. They are opposites: Partial makes every property optional, while Required makes every property mandatory. Understanding both — and when to reach for each — will sharpen your API design immediately.

Partial<T>

Partial<T> takes a type T and produces a new type where every property is marked optional with ?. The original type is not modified.

TS
interface User {
  id: number;
  name: string;
  email: string;
  role: "admin" | "editor" | "viewer";
}

type PartialUser = Partial<User>;
/*
{
  id?: number;
  name?: string;
  email?: string;
  role?: "admin" | "editor" | "viewer";
}
*/
Note
Every property becomes optional — none are required. The value types are preserved exactly as-is; only the optionality changes.
How Partial Is Implemented

Under the hood, Partial<T> is a mapped type that iterates over every key of T and adds ?:

TS
// TypeScript's built-in definition (from lib.es5.d.ts)
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// You could write the same thing manually:
type MyPartial<T> = {
  [P in keyof T]?: T[P];
};

// They are functionally identical
type A = Partial<User>;
type B = MyPartial<User>;
// A and B are the same type
When to Use Partial

The most common use cases for Partial<T>:

  • PATCH / update functions — the caller only supplies the fields they want to change.

  • Default / config objects — allow partial overrides that get merged with defaults.

  • Form state before validation — fields start empty and fill in as the user types.

  • Test fixtures — supply only the properties relevant to the test, let the rest default.

  • Builder patterns — accumulate properties one at a time before finalizing.

Update Function Pattern

TS
interface User {
  id: number;
  name: string;
  email: string;
  role: "admin" | "editor" | "viewer";
}

// In-memory store
const users = new Map<number, User>();

// Partial<User> so callers supply only the fields they want to change
function updateUser(id: number, patch: Partial<User>): User {
  const existing = users.get(id);
  if (!existing) throw new Error(`User ${id} not found`);

  const updated: User = { ...existing, ...patch, id }; // id is always preserved
  users.set(id, updated);
  return updated;
}

// Valid — only updating the name
updateUser(1, { name: "Alice" });

// Valid — updating multiple fields at once
updateUser(1, { name: "Bob", role: "admin" });
Config Objects with Defaults

TS
interface ServerConfig {
  host: string;
  port: number;
  maxConnections: number;
  timeout: number;
  debug: boolean;
}

const DEFAULT_CONFIG: ServerConfig = {
  host: "localhost",
  port: 3000,
  maxConnections: 100,
  timeout: 5000,
  debug: false,
};

// Callers only need to provide the fields they want to override
function createServer(options: Partial<ServerConfig> = {}): ServerConfig {
  return { ...DEFAULT_CONFIG, ...options };
}

const server = createServer({ port: 8080, debug: true });
// { host: "localhost", port: 8080, maxConnections: 100, timeout: 5000, debug: true }
Tip
This pattern is ubiquitous in library configuration: the library defines full defaults, and users override only what they need with a Partial argument.
Required<T>

Required<T> is the mirror image of Partial<T>. It takes a type T and produces a new type where every property is required — all ? modifiers are removed.

TS
interface UserForm {
  name?: string;
  email?: string;
  password?: string;
}

type ValidatedUser = Required<UserForm>;
/*
{
  name: string;
  email: string;
  password: string;
}
*/

// This now requires all three fields — no optionals
function createUser(data: ValidatedUser): void {
  console.log(`Creating user: ${data.name} <${data.email}>`);
}
How Required Is Implemented

Required<T> uses the -? mapped type modifier, which strips the optional marker from each property:

TS
// TypeScript's built-in definition
type Required<T> = {
  [P in keyof T]-?: T[P];
};

// The -? syntax removes the ? modifier
// Compare with +? which adds it (what Partial uses internally)

// You can also use -readonly to remove readonly modifiers:
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};
When to Use Required
  • After validation — convert a partially filled form type into a fully populated entity.

  • Internal vs external APIs — accept optional fields externally, require them internally after defaulting.

  • Testing — assert that a function filled in all expected fields.

  • Ensuring all config options are resolved before use.

Form Validation Pattern

TS
interface SignupForm {
  name?: string;
  email?: string;
  password?: string;
  acceptedTerms?: boolean;
}

type ValidSignup = Required<SignupForm>;

function validate(form: SignupForm): form is ValidSignup {
  return (
    typeof form.name === "string" &&
    typeof form.email === "string" &&
    typeof form.password === "string" &&
    form.acceptedTerms === true
  );
}

function handleSignup(raw: SignupForm): void {
  if (!validate(raw)) {
    console.error("Form is incomplete");
    return;
  }
  // raw is now narrowed to ValidSignup — all fields present
  console.log(`Welcome, ${raw.name}!`);
}
Partial vs Required — Side by Side

Scenario

Use Partial

Use Required

HTTP PATCH endpoint

Yes — only changed fields needed

No

HTTP POST endpoint (create)

No

Yes — all fields needed

Library config with defaults

Yes — callers override selectively

No

Post-validation entity

No

Yes — all fields guaranteed

Optional builder accumulation

Yes

No

Final builder result

No

Yes

Combining Partial and Required

You can combine them to make some fields required and others optional. Use intersection types or Pick/Omit to split the type first:

TS
interface Article {
  id: number;
  title: string;
  body: string;
  tags?: string[];
  publishedAt?: Date;
}

// id and title are always required in updates; everything else is optional
type ArticleUpdate = Required<Pick<Article, "id" | "title">> &
  Partial<Omit<Article, "id" | "title">>;

function updateArticle(update: ArticleUpdate): void {
  console.log(`Updating article ${update.id}: ${update.title}`);
}

// id and title must be provided:
updateArticle({ id: 1, title: "Hello" });             // ok
updateArticle({ id: 2, title: "World", tags: ["ts"] }); // ok
// updateArticle({ title: "No ID" });                 // Error: id is missing
Practical Pattern: Staged Builder

TS
interface EmailMessage {
  to: string;
  subject: string;
  body: string;
  cc?: string;
  bcc?: string;
}

class EmailBuilder {
  private data: Partial<EmailMessage> = {};

  to(address: string): this {
    this.data.to = address;
    return this;
  }

  subject(s: string): this {
    this.data.subject = s;
    return this;
  }

  body(b: string): this {
    this.data.body = b;
    return this;
  }

  cc(address: string): this {
    this.data.cc = address;
    return this;
  }

  build(): EmailMessage {
    const { to, subject, body } = this.data;
    if (!to || !subject || !body) {
      throw new Error("to, subject, and body are required");
    }
    return this.data as EmailMessage; // safe after validation
  }
}

const email = new EmailBuilder()
  .to("alice@example.com")
  .subject("Hello!")
  .body("Nice to meet you.")
  .build();

console.log(email.to); // "alice@example.com"
alice@example.com
Warning
Partial and Required are both shallow — they only affect the top level of the type. If you need to affect nested properties too, write a DeepPartial or DeepRequired custom mapped type.
Quick Summary

Utility

Modifier Used

Effect

Partial<T>

+? on all keys

Every property becomes optional

Required<T>

-? on all keys

Every property becomes required

Readonly<T>

+readonly on all keys

Every property becomes read-only

Mutable<T> (custom)

-readonly on all keys

Removes all readonly modifiers

Success
Use Partial when you accept a subset of fields (updates, configs, fixtures) and Required when you need to guarantee all fields are present (validated entities, finalized builders).