Immutable Builders
Return new builder instances for safer composition.
Immutable Builders is a free TypeScript Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Mutation vs Immutability
The builders so far mutate this and return it. An immutable builder instead returns a new builder instance on every step, never changing the original. This makes composition safer.
The Problem with Shared Mutation
If two parts of your code hold the same mutable builder, one setter call affects both. Immutable builders avoid this aliasing surprise entirely.
const base = makeMutableBuilder().url("/x");
// If base is shared, later .method() calls mutate everyone's copy.Returning a New Instance
An immutable setter copies the current state, adds the new field, and constructs a fresh builder. The original is untouched.
class IB<S> {
constructor(private readonly d: Record<string, unknown> = {}) {}
with<K extends string, V>(k: K, v: V): IB<S & Record<K, V>> {
return new IB<S & Record<K, V>>({ ...this.d, [k]: v });
}
}Structural Sharing via Spread
The spread { ...this.d, [k]: v } copies existing entries and overrides one key. Unchanged values are shared by reference, so copies stay cheap.
const a = new IB<{}>().with("url", "/x");
const c = a.with("method", "GET");
// a still only has url; c has url and method.Originals Never Change
Because each step yields a new object, branching from an earlier builder is safe. You can reuse a partially-built base in multiple places.
const base = new IB<{}>().with("url", "/api");
const get = base.with("method", "GET");
const post = base.with("method", "POST");
console.log("base untouched; get and post diverge");Readonly Internal State
Marking the internal data readonly documents the intent and stops accidental in-place writes inside the class. The only way forward is to create a new instance.
class IB<S> {
constructor(private readonly d: Readonly<Record<string, unknown>> = {}) {}
}Typed Immutable Build
The state parameter still threads through, so we can gate build() exactly as before, now on an immutable foundation.
interface Out { url: string; method: string }
class IB<S> {
constructor(private readonly d: Record<string, unknown> = {}) {}
with<K extends string, V>(k: K, v: V): IB<S & Record<K, V>> {
return new IB<S & Record<K, V>>({ ...this.d, [k]: v });
}
build(this: IB<{ url: string; method: string }>): Out { return this.d as Out; }
}
const out = new IB<{}>().with("url", "/u").with("method", "GET").build();
console.log(out.url);Safer Composition
Immutable builders compose like values: pass one around, derive new ones, and never worry that a downstream call rewrote shared state. This is the same benefit immutable data gives everywhere.
Reusable Presets
You can capture a common starting point once and fork it many times. Each fork is independent thanks to the copy-on-write setters.
const jsonBase = new IB<{}>().with("header", "application/json");
const usersReq = jsonBase.with("url", "/users");
const ordersReq = jsonBase.with("url", "/orders");
console.log("two independent requests from one base");Cost Considerations
Each step allocates a new object. For typical config building this is negligible, and structural sharing keeps copies shallow. Only extremely hot loops would notice.
Mutable vs Immutable Summary
Mutable builders are slightly faster and simpler; immutable builders are safer to share and reason about. Prefer immutable when builders are passed around or reused as presets.
Quick Check
Quick check on this lesson.
Recap
An immutable builder returns a new instance per step using { ...this.d, [k]: v } structural sharing, never mutating this. Originals stay intact, enabling safe composition and reusable presets, with only minor allocation cost.
Frequently asked questions
Is the “Immutable Builders” lesson free?
Yes — the full text of “Immutable Builders” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.
What will I learn in “Immutable Builders”?
Return new builder instances for safer composition. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start TypeScript Academy?
No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Immutable Builders” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this TypeScript Academy lesson?
Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.