← Back to TypeScript Course | Chapter 16: Advanced Patterns | Lesson 1 of 7

Builder Pattern

The Builder Pattern creates complex objects step by step instead of requiring one large constructor call. It is useful when an object has many optional settings or when construction should be easy to read.

Basic Builder

A basic builder is a class that accumulates configuration across multiple method calls before a final .build() method assembles and returns the fully constructed object, instead of a single large constructor taking every option at once.

Example: Basic Builder

typescript
class PizzaBuilder {
  private toppings: string[] = [];
  addTopping(topping: string): this {
    this.toppings.push(topping);
    return this;
  }
  build(): string {
    return `Pizza with ${this.toppings.join(", ")}`;
  }
}
console.log(new PizzaBuilder().addTopping("cheese").build());

Fluent Methods

Fluent methods on a builder each return this, which is what allows chaining calls together — new Builder().setA(1).setB(2).build() — into one readable expression instead of separate statements.

Example: Fluent Methods

typescript
class RequestBuilder {
  private url = "";
  private method = "GET";
  setUrl(url: string): this { this.url = url; return this; }
  setMethod(method: string): this { this.method = method; return this; }
  build() { return `${this.method} ${this.url}`; }
}
console.log(new RequestBuilder().setUrl("/api").setMethod("POST").build());

Optional Values

Marking builder fields optional in the internal state models values that don't have to be set before calling .build(), letting the builder supply sensible defaults for whatever the caller left unspecified.

Example: Optional Values

typescript
class UserBuilder {
  private name = "";
  private age?: number;
  setName(name: string): this { this.name = name; return this; }
  setAge(age: number): this { this.age = age; return this; }
  build() { return { name: this.name, age: this.age ?? 0 }; }
}
console.log(new UserBuilder().setName("Ravi").build());

Validation

A builder's .build() method is a natural place to validate that all required fields were actually set, throwing a clear error before an invalid object gets constructed rather than after.

Example: Validation

typescript
class UserBuilder {
  private name?: string;
  setName(name: string): this { this.name = name; return this; }
  build() {
    if (!this.name) throw new Error("name is required");
    return { name: this.name };
  }
}
console.log(new UserBuilder().setName("Ravi").build());

When to Use Builder

Reach for the builder pattern when an object has many optional configuration fields — it avoids both an unwieldy multi-argument constructor and an easy-to-misuse partially-filled object passed around before it's ready.

Example: When to Use Builder

typescript
class QueryBuilder {
  private parts: string[] = [];
  select(cols: string): this { this.parts.push(`SELECT ${cols}`); return this; }
  from(table: string): this { this.parts.push(`FROM ${table}`); return this; }
  build() { return this.parts.join(" "); }
}
console.log(new QueryBuilder().select("*").from("users").build());
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.