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