Search code examples
typescriptbuilder

How can I type this object builder in TypeScript strict mode without bloating it?


I like this builder pattern I found online, but in strict mode it won't work because I get for the first 3 properties the same error as the first:

(property) PizzaBuilder.numberOfSlices: number
Property 'numberOfSlices' has no initializer and is not definitely assigned in the constructor.ts(2564)
export enum DoughType {
  HEALTHY,
}
export enum Topping {
  CHEESE,
}
export interface Pizza {
  numberOfSlices: number;
  isThin: boolean;
  doughType: DoughType;
  toppings: Topping[];
}

export class PizzaBuilder {
  private numberOfSlices: number;
  private isThin: boolean;
  private doughType: DoughType;
  private toppings: Topping[] = [];

  public setNumberOfSlices(numberOfSlices: number): PizzaBuilder {
    this.numberOfSlices = numberOfSlices;
    return this;
  }
  public setIsThin(isThin: boolean): PizzaBuilder {
    this.isThin = isThin;
    return this;
  }
  public setDoughType(doughType: DoughType): PizzaBuilder {
    this.doughType = doughType;
    return this;
  }
  public addTopping(topping: Topping): PizzaBuilder {
    this.toppings.push(topping);
    return this;
  }
  public build(): Pizza {
    if (this.isThin === undefined) this.isThin = false;
    if (this.numberOfSlices === undefined) this.numberOfSlices = 8;
    if (this.doughType === undefined) throw new Error('Dough type must be set');
    if (this.toppings.length < 1) this.toppings.push(Topping.CHEESE);

    return {
      numberOfSlices: this.numberOfSlices,
      isThin: this.isThin,
      toppings: this.toppings,
      doughType: this.doughType,
    };
  }
}

const pizza = new PizzaBuilder()
  .setIsThin(true)
  .setNumberOfSlices(6)
  .setDoughType(DoughType.HEALTHY)
  .addTopping(Topping.CHEESE)
  .build();

I want to avoid giving numberOfSlices, isThin and doughType default values as it seems to defeat the idea behind the builder. I can't set them to undefined as that won't work.

Is there a solution that avoids excessive bloat? Adding booleans to detect if something has been set seems like a cruft nightmare.


Solution

  • TypeScript is complaining because in strict mode, undefined is not assignable to types number, boolean, or DoughType, and in strict mode, each class property must be initialized with a value of its type.

    Since you're meaning for these properties to be potentially undefined, you can explicitly type them with a union type that includes undefined as a valid value:

    private numberOfSlices: number | undefined;
    private isThin: boolean | undefined;
    private doughType: DoughType | undefined;
    private toppings: Topping[] = [];
    

    There is more about Strict Class Initialization here in the TypeScript 2.7 relase notes.