Skip to content

Instantly share code, notes, and snippets.

@Nikamura
Last active November 16, 2020 09:49
Show Gist options
  • Save Nikamura/81b84673677bacf70f80b3b6a093de5c to your computer and use it in GitHub Desktop.
Save Nikamura/81b84673677bacf70f80b3b6a093de5c to your computer and use it in GitHub Desktop.
Fluent builder pattern in TypeScript with type support
// Types Except & SetRequired from https://github.com/sindresorhus/type-fest
type Except<ObjectType, KeysType extends keyof ObjectType> = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>;
type SetRequired<BaseType, Keys extends keyof BaseType = keyof BaseType> = Except<BaseType, Keys> &
Required<Pick<BaseType, Keys>> extends infer InferredType
? { [KeyType in keyof InferredType]: InferredType[KeyType] }
: never;
class ChainableBuilder<T, X> {
constructor(public args: T, public x: new (...args: any[]) => X) {}
public set<K extends keyof T>(
key: K,
value: string,
): SetRequired<T, K> extends Required<T>
? ChainableBuilder<SetRequired<T, K>, X>
: Omit<ChainableBuilder<SetRequired<T, K>, X>, "build"> {
return new this.constructor({ ...this.args, [key]: value }, this.x);
}
public build(): X {
return new this.x(this.args);
}
}
class User {
public email: string;
private _password: string;
constructor(args: Required<IUserBuilderOpts>) {
this.email = args.email;
this._password = args.password;
}
}
interface IUserBuilderOpts {
email?: string;
password?: string;
}
const builder = new ChainableBuilder<IUserBuilderOpts, User>({}, User);
builder.set("email", "value").build(); // Property 'build' does not exist on type 'Pick<ChainableBuilder<{ password?: string | undefined; email: string; }, User>, "args" | "x" | "set">'.ts(2339)
builder.set("email", "value").set("email", "value").build(); // Property 'build' does not exist on type 'Pick<ChainableBuilder<{ password?: string | undefined; email: string; }, User>, "args" | "x" | "set">'.ts(2339)
builder.set("email", "value").set("password", "value").build(); // works
builder.build();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment