Skip to content

Instantly share code, notes, and snippets.

@acidstorm
Last active December 14, 2023 22:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acidstorm/f201036811255411df8bafb16c99c7c6 to your computer and use it in GitHub Desktop.
Save acidstorm/f201036811255411df8bafb16c99c7c6 to your computer and use it in GitHub Desktop.
Fun with builders in TS
// Fun with the builder pattern
type ObjectBuilder<T> = {
[K in keyof T as Setter<K>]: (value: T[K]) => void
} & {
[K in keyof T as Getter<K>]: () => T[K]
}
& Builder<T>
type Setter<T> = T extends infer U extends string ? `set${Capitalize<U>}` : never
type Getter<T> = T extends infer U extends string ? `get${Capitalize<U>}` : never
type Builder<T> = { build: () => T }
const createBuilder = <T>() => {
const builder: ObjectBuilder<T> = {} as ObjectBuilder<T>
function getPropertyFromGetterSetter(property: string) {
const sliced = property.slice(3);
const firstLetter = sliced[0].toLowerCase();
const rest = sliced.slice(1);
return firstLetter + rest;
}
const proxyHandler = {
get(target, prop) {
if (prop === 'build') {
return () => structuredClone(builder)
}
if (prop.startsWith("set")) {
return (newValue) => {
target[getPropertyFromGetterSetter(prop)] = newValue;
};
}
if (prop.startsWith("get")) {
return () => target[getPropertyFromGetterSetter(prop)];
}
},
};
return new Proxy(builder, proxyHandler) as typeof builder
}
type K = { item: string, classic: boolean }
class L {
items: string
classics: boolean
}
const f = createBuilder<K>()
f.setClassic(false)
const initialF = f.build()
f.setClassic(true)
const postUpdateF = f.build()
const g = createBuilder<L>()
const initialG = g.build()
console.log({ initialF, postUpdateF, initialG })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment