Skip to main content

สร้าง Builder Pattern แบบ Type-Safe

Basic

class Inventory<Items extends Record<string, unknown> = {}> {

items: Items = {} as Items;

add<NewItem extends Record<string, unknown>>(value: NewItem) {
this.items = {
...value as unknown as Items
}
return this as Inventory<Items & NewItem>;
}
}

const inventory = new Inventory()
.add({
hello: 'world',
}).add({
typescript: 5.1,
numbers: [23, '123']
});

console.log(inventory.items.typescript)


type A = typeof inventory.items;

// type A = {
// hello: string;
// } & {
// typescript: number;
// numbers: (string | number)[];
// }

playground

Ref: TypeScript Meetup Thailand July 2023 https://www.facebook.com/phantipk/videos/289991566938840?idorvanity=1377224595918442

More advanced

class ObjectBuilder<Items extends Record<string, unknown> = {}> {

constructor(private readonly jsonObject: Items) { }

add<K extends string, V>(key: K, value: V) {
const nextPart = { [key]: value } as Record<K, V>;
return new ObjectBuilder({ ...this.jsonObject, ...nextPart }) as
ObjectBuilder<{ [Key in keyof (Items & Record<K, V>)]: (Items & Record<K, V>)[Key] }>;
}

build(): Items {
return this.jsonObject;
}

static create(){
return new ObjectBuilder({});
}

}

const json = ObjectBuilder.create()
.add('aString', 'some text')
.add('aNumber', 2)
.add('anArray', [1, 2, 3])
.build();

type B = typeof json;

// type B = {
// aString: string;
// aNumber: number;
// anArray: number[];
// }

playground

Ref: https://medium.hexlabs.io/the-builder-pattern-with-typescript-using-advanced-types-e05a03ffc36e