HardPro challengeJavaScriptTypeScript

Type-safe Builder Pattern

TypeScriptTypesPatterns

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">'

Implement RequestBuilder

MethodBehavior
.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

Test #1minimal GET request
Input: [[["method","GET"],["url","https://api.example.com/challenges"],["build"]]]
Output: {"url":"https://api.example.com/challenges","method":"GET","headers":{}}
Test #2POST request with body
Input: [[["method","POST"],["url","https://api.example.com/submit"],["body",{"code":"fn(){}"}],["build"]]]
Output: {"url":"https://api.example.com/submit","body":{"code":"fn(){}"},"method":"POST","headers":{}}
Test #3request with multiple headers
Input: [[["method","GET"],["url","/api"],["header","Authorization","Bearer token123"],["header","Accept","application/json"],["build"]]]
Output: {"url":"/api","method":"GET","headers":{"Accept":"application/json","Authorization":"Bearer token123"}}