typestar

A typed builder in TypeScript

The generic tracks which fields have been supplied so far.

interface Query {
  table: string;
  where?: string;
  limit?: number;
}

class QueryBuilder<Have extends keyof Query = never> {
  private parts: Partial<Query> = {};

  from(table: string): QueryBuilder<Have | "table"> {
    this.parts.table = table;
    return this as QueryBuilder<Have | "table">;
  }

  where(clause: string): QueryBuilder<Have | "where"> {
    this.parts.where = clause;
    return this as QueryBuilder<Have | "where">;
  }

  build(this: QueryBuilder<"table" | Have>): string {
    const { table, where, limit } = this.parts;
    return [`SELECT * FROM ${table}`, where && `WHERE ${where}`,
            limit && `LIMIT ${limit}`].filter(Boolean).join(" ");
  }
}

console.log(new QueryBuilder().from("runs").where("tpm > 90").build());
// new QueryBuilder().where("x").build(); // error: table never set

How it works

  1. Each setter widens the accumulated key set.
  2. build only typechecks once the required keys are present.
  3. The compiler enforces the order you actually need.

Keywords and builtins used here

The run, in numbers

Lines
28
Characters to type
770
Tokens
196
Three-star pace
110 tpm

At the three-star pace of 110 tokens a minute, this run takes about 107 seconds.

Type this snippet

Step 1 of 5 in Building & injecting, step 9 of 19 in Classes & patterns.

← Previous Next →