The Builder pattern constructs complex objects step-by-step. In TypeScript, you can make it type-safe so that calling .build() without required fields is a compile error:
// The trick: use a generic type parameter to track which fields are set
class Builder<TSet extends string = never> {
method(m: HttpMethod): Builder<TSet | 'method'> { ... }
url(u: string): Builder<TSet | 'url'> { ... }
// 'this' parameter — TypeScript checks the call site, not the implementation
build(this: Builder<TSet & ('method' | 'url')>): RequestConfig { ... }
}
new RequestBuilder()
.method('GET')
.url('/api/challenges')
.build(); // ✅
new RequestBuilder()
.method('GET')
.build(); // ❌ TypeScript Error: Property 'url' is missing in type 'Builder<"method">'RequestBuilder| Method | Behavior |
|---|---|
.method(m) | Sets HTTP method |
.url(u) | Sets request URL |
.header(key, value) | Adds a header (chainable, multiple calls) |
.body(b) | Sets request body |
.build() | Returns the final RequestConfig object |
.method() and .url() are required before .build(). The TypeScript constraint is in the this parameter of build().
Sample tests