Implement production TypeScript patterns: Builder, Observer, Command, Strategy, Repository with generics, type-safe event emitter, and the Fluent Builder pattern.
Time
35 minutes
Prerequisites
Lab 04 (Classes), Lab 06 (Generics)
Tools
Docker image: zchencow/innozverse-ts:latest
Lab Instructions
Step 1: Fluent Builder Pattern
// Immutable fluent builder using genericsclassQueryBuilder<TextendsRecord<string,unknown>>{private conditions:string[] = [];private _limit?:number;private _offset?:number;private _orderBy?:{ field:keyofT; direction:"ASC"|"DESC"};private _fields?: (keyofT)[];constructor(privatetable:string){}select(...fields: (keyofT)[]):this{this._fields = fields;returnthis;}where(field:keyofT,op:"="|">"|"<"|">="|"<="|"LIKE",value:unknown):this{this.conditions.push(`${String(field)}${op}${JSON.stringify(value)}`);returnthis;}limit(n:number):this{this._limit = n;returnthis;}offset(n:number):this{this._offset = n;returnthis;}orderBy(field:keyofT,direction:"ASC"|"DESC"="ASC"):this{this._orderBy ={ field, direction };returnthis;}build():string{constfields=this._fields?.map(String).join(", ") ??"*";let sql =`SELECT ${fields} FROM ${this.table}`;if (this.conditions.length) sql +=` WHERE ${this.conditions.join(" AND ")}`;if (this._orderBy) sql +=` ORDER BY ${String(this._orderBy.field)}${this._orderBy.direction}`;if (this._limit) sql +=` LIMIT ${this._limit}`;if (this._offset) sql +=` OFFSET ${this._offset}`;return sql;}}interfaceProduct{id:number;name:string;price:number;category:string;stock:number;}constquery=newQueryBuilder<Product>("products").select("id","name","price").where("category","=","Laptop").where("price","<",1000).orderBy("price","DESC").limit(10).offset(0).build();console.log("SQL:",query);
💡 keyof T in Builder ensures you can only reference actual properties of the target type. where("invalid_column", ...) would be a compile error. This makes the query builder safe — misspelled column names are caught at development time, not at runtime when the query fails.
📸 Verified Output:
Step 2: Observer / Event Emitter Pattern
💡 Returning an unsubscribe function from on() is the modern pattern (React hooks use this). The alternative is emitter.off(event, listener) but it requires keeping a reference to the exact function. The returned () => void closure captures the reference, so callers don't need to.
📸 Verified Output:
Steps 3–8: Command, Strategy, Repository, Proxy, State Machine, Capstone
📸 Verified Output:
Summary
TypeScript patterns elevate code quality dramatically. You've implemented a type-safe fluent QueryBuilder, typed EventEmitter with unsubscribe, Command pattern with undo/redo, Strategy pattern, Specification pattern for filtering, logging Proxy, and a typed state machine. These patterns are in Angular, NestJS, RxJS, and every large TypeScript codebase.