A TypeScript builder pattern library with compile-time type safety and zero boilerplate. Extend an auto builder and you get an automatic, fully typed setter for every property β and build() is a compile error until every required property has been set.
- Zero Boilerplate:
CeriosAutoBuilder<T>()/CeriosClassAutoBuilder(Class)generate every setter β no hand-written wrappers - Type-Safe Building: Compile-time validation ensures all required properties are set before
build()compiles - Class Support: Build real class instances β methods, getters, decorators, and prototype chains preserved
- Flexible Runtime Validation: Required-field checks and custom validators, independent of the compile-time tracking
- Immutability Options: Frozen or sealed results, shallow or deep
- Composable: Nested-builder callbacks and the director pattern for deep structures
- Immutable Builders: Every setter is copy-on-write β forked builders never affect each other
- TypeScript First: Built with TypeScript, for TypeScript
npm install @cerios/cerios-builderdev install
npm install --save-dev @cerios/cerios-builderimport { CeriosAutoBuilder } from "@cerios/cerios-builder";
// 1. Define your type
type User = {
id: string;
name: string;
email: string;
role: string;
age?: number;
};
// 2. Extend the auto builder - every property gets a setter automatically
class UserBuilder extends CeriosAutoBuilder<User>() {
static create(): UserBuilder {
return new UserBuilder({});
}
// Custom logic is an ordinary method delegating to a generated setter
asAdmin() {
return this.role("admin");
}
}
// 3. Build - all required properties enforced at compile time
const user = UserBuilder.create().id("1").name("Alice").email("a@b.io").asAdmin().build();
// β Compile error that names what is missing:
// UserBuilder.create().id("1").name("Alice").build();
// The 'this' context of type '...' is not assignable to method's 'this' of type
// 'MissingRequiredProperties<"email" | "role">'.
// β
Optional properties can be omitted (age is optional)Every property becomes a bare setter (id, name, β¦). Custom methods use those setters on this and mix freely into the chain, each contributing to the compile-time required-field tracking. If the type has no required properties at all, build() is available immediately.
The setters are linked to your type in the editor: Ctrl+Click (Go to Definition) on .name(...) opens name in User, hovering it shows that property's documentation comment, and renaming the property also renames the setter calls.
Building instantiates the real class, so methods, getters, and decorators are preserved. Setters are generated for writable data properties only: methods, getter-only accessors, and readonly fields get no setter and don't count toward the required-property check β a class's readonly fields are seeded through its own constructor (see Limitations):
import { CeriosClassAutoBuilder } from "@cerios/cerios-builder";
class Person {
name!: string;
age!: number;
email?: string;
constructor(data?: Partial<Person>) {
if (data) Object.assign(this, data);
}
greet(): string {
return `Hi, ${this.name}`;
}
}
class PersonBuilder extends CeriosClassAutoBuilder(Person) {
static create(): PersonBuilder {
return new PersonBuilder();
}
}
const person = PersonBuilder.create().name("Alice").age(30).build();
person.greet(); // "Hi, Alice"
person instanceof Person; // trueLeave the return type off and TypeScript infers it, so the brand keeps flowing and build() stays fully checked. If your lint requires explicit return types, use BuilderWith<> (ClassBuilderWith<> for class builders). It takes the builder and the keys the method sets β the built type is derived from the builder, so you never repeat it:
import { BuilderWith, CeriosAutoBuilder } from "@cerios/cerios-builder";
class AddressBuilder extends CeriosAutoBuilder<Address>() {
static create(): AddressBuilder {
return new AddressBuilder({});
}
static createWithDefaults(): BuilderWith<AddressBuilder, "country"> {
return AddressBuilder.create().country("NL");
}
static createComplete(): BuilderWith<AddressBuilder> {
return AddressBuilder.create().country("NL").street("Main St").city("Rotterdam");
}
inRotterdam(): BuilderWith<this, "city", AddressBuilder> {
return this.city("Rotterdam");
}
}| What the method does | Return type |
|---|---|
| Factory, nothing preset | AddressBuilder |
| Factory presetting some fields | BuilderWith<AddressBuilder, "country"> |
| Factory presetting everything | BuilderWith<AddressBuilder> |
| Custom method setting one field | BuilderWith<this, "city", AddressBuilder> |
| Custom method setting several | BuilderWith<this, "city" | "street", AddressBuilder> |
| Class builders | the same, with ClassBuilderWith<> |
Instance methods start with this. this keeps everything set earlier in the chain; naming the class there instead would return a plain AddressBuilder and discard that, so build() would stop compiling.
The third argument, the builder class, is only where the property names are looked up β that is what makes them autocomplete inside the quotes. BuilderWith<this, "city"> describes exactly the same type and keeps working, but editors cannot list the property names for this, so there are no suggestions until the class is added. Existing two-argument methods only need the class appended. In a new method, type the class first (BuilderWith<this, "", AddressBuilder>), then fill in the quotes.
Tip: VS Code only suggests inside quotes when you type the opening quote or press Ctrl+Space. To get suggestions while typing inside a string, set
"editor.quickSuggestions": { "strings": "on" }.
Fields you add to a builder subclass (a Map cache, a counter) survive every setter call β but they are carried by reference: each setter returns a copy-on-write fork, and all forks and clones share the same field instances. Only the target data is deep-cloned. Keep anything fork-specific in the target data itself; builder fields are for genuinely shared state such as memoized lookups.
A custom method cannot reuse a property's exact name β the method would shadow the generated setter it needs to call. Give it a distinct name and delegate:
trimmedName(value: string): BuilderWith<this, "name"> {
return this.name(value.trim());
}
addTag(tag: string): BuilderWith<this, "tags"> {
return this.tags([...(this.buildPartial().tags ?? []), tag]);
}-
A property whose name collides with a builder method β
build,clone,then,toJSON,setRequiredFields,addValidator, and the other builder members β gets aProp-suffixed setter instead, and the real method keeps its name:type Config = { url: string; build: number }; class ConfigBuilder extends CeriosAutoBuilder<Config>() { /* ... */ } ConfigBuilder.create().url("/x").buildProp(3).build(); // buildProp sets `build`; build() still builds
-
A property whose name isn't a valid identifier (for example
"content-type") keeps its exact name and is set with bracket access β fully type-safe:builder["content-type"]("application/json");
Setters are generated from keyof T, which includes everything inherited β so a builder for a derived type automatically has setters for the base properties too. No base builder is needed just to share setters:
type BasePostRequest = { postId: string; title?: string; tags?: string[] };
type CreatePostRequest = BasePostRequest & { title: string; authorId: string };
class CreatePostRequestBuilder extends CeriosAutoBuilder<CreatePostRequest>() {}
// .postId(), .title(), .tags() and .authorId() all exist, fully typedThe same holds for class hierarchies: CeriosClassAutoBuilder(Order) where class Order extends BaseEntity generates setters for the inherited data properties, and building runs the real constructor chain β the result is instanceof both classes with all inherited methods intact.
Custom methods you want to reuse across several derived types go in a base-builder function: a function constrained on AutoBuilderBase<TBase> (or ClassAutoBuilderBase<TBase> for classes) that layers the shared methods on top of each concrete auto builder. Name it after the abstract base builder class you would have written by hand β the extends clause then reads the same:
import { AutoBuilderBase, BuilderExtension, CeriosAutoBuilder } from "@cerios/cerios-builder";
function BasePostRequestBuilder<TBuilder extends AutoBuilderBase<BasePostRequest>>(Builder: TBuilder) {
abstract class PostRequestBuilder extends Builder {
addTag(tag: string) {
return this.tags([...(this.buildPartial().tags ?? []), tag]);
}
}
return PostRequestBuilder as BuilderExtension<TBuilder, PostRequestBuilder>;
}
class CreatePostRequestBuilder extends BasePostRequestBuilder(CeriosAutoBuilder<CreatePostRequest>()) {
static create(): CreatePostRequestBuilder {
return new CreatePostRequestBuilder({}, { requiredFields: ["postId", "title", "authorId"] });
}
}Inside the function, this exposes a setter for every property of the base type plus the brand-free part of the build/state API: buildPartial(), buildUnsafe(), buildWithoutCompileTimeValidation(), clone(), and addValidator() β so shared methods can fork, validate, and build. (The compile-gated variants like build() need the derived builder's brand and stay derived-only.) Each derived builder keeps its own full API: all setters, per-builder required fields, and the static from(). Setter calls in shared methods brand the build gate like any other setter, so create().addTag("x") counts toward compile-time validation β and if your lint requires explicit return types, BuilderWith<this, "tags"> (or ClassBuilderWith<>) works on shared methods as in Custom Methods. Use the two-argument this form here: inside a base-builder function there is no concrete builder class to name. The BuilderExtension<TBuilder, TShared> return type is what keeps the derived setters and statics visible on the result β don't leave it off.
For class hierarchies the shape is identical; the base class may be abstract, because it is only used as a type:
import { BuilderExtension, CeriosClassAutoBuilder, ClassAutoBuilderBase } from "@cerios/cerios-builder";
abstract class BaseEntity {
id!: string;
createdAt?: Date;
}
function BaseEntityBuilder<TBuilder extends ClassAutoBuilderBase<BaseEntity>>(Builder: TBuilder) {
abstract class EntityBuilder extends Builder {
createdNow() {
return this.createdAt(new Date());
}
}
return EntityBuilder as BuilderExtension<TBuilder, EntityBuilder>;
}
class OrderBuilder extends BaseEntityBuilder(CeriosClassAutoBuilder(Order)) {}
class CustomerBuilder extends BaseEntityBuilder(CeriosClassAutoBuilder(Customer)) {}One nuance: when a derived type strengthens an optional base property to required (like title above), a shared method whose return type is inferred brands the base type's optional flavor, which doesn't count toward the derived build gate. Annotating the shared method with BuilderWith<this, "title"> (or ClassBuilderWith<>) fixes this: BuilderWith re-resolves the brand against the concrete builder's own target at the call site, so the same call satisfies the strengthened gate. Prefer the annotation on any shared method that sets a property a derived type might strengthen.
Compile-time tracking works on its own. On top of it, both auto builders accept the same constructor options for runtime validation, so a subclass declares everything once:
class UserBuilder extends CeriosAutoBuilder<User>() {
constructor(data?: Partial<User>) {
super(data, {
requiredFields: { id: true, name: true, email: true, role: true },
validators: [(u): boolean | string => (u.email?.includes("@") ? true : "Invalid email")],
});
}
static create(): UserBuilder {
return new UserBuilder();
}
}The record form ({ id: true, ... }) is exhaustive: add a required property to User and this line fails to compile, so runtime validation cannot silently fall behind. The array form is validated against your type but not checked for completeness β use it when you need nested dot-notation paths:
super(data, {
requiredFields: ["order.details.customerId", "order.details.shippingAddress.city"],
});Deriving the list from the type alone is not possible β types are erased before runtime, so the keys must exist as a value somewhere.
When the list depends on data you only have at runtime:
const builder = employee.isContractor
? base.setRequiredFields(["id", "name", "contractEndDate"])
: base.setRequiredFields(["id", "name", "employeeId"]);setRequiredFields() replaces the instance list β the last call wins, so pass one complete list per call. It is copy-on-write like every other builder method.
Validators receive the partial object and return true (pass), false (generic failure), or a string (failure with message):
const user = UserBuilder.create()
.id("1")
.name("Bob")
.email("bob@example.com")
.asAdmin()
.addValidator((obj) => (obj.age ?? 18) >= 18 || "User must be at least 18 years old")
.addValidator((obj) => obj.email?.includes("@") || "Invalid email format")
.build();- Validators run on
build(),buildWithoutCompileTimeValidation(), and the frozen/sealed variants;buildUnsafe()andbuildPartial()skip them. - Every validator runs; all failure messages are joined into a single error.
- A validator returning
falseis identified by index and function name; returningundefined(a forgottenreturn) or an empty string is treated as a failure, not a pass.
Every validating build variant throws a CeriosBuilderError. It extends Error, and carries the failure as data:
import { CeriosBuilderError } from "@cerios/cerios-builder";
try {
builder.build();
} catch (error) {
if (error instanceof CeriosBuilderError) {
error.missingFields; // e.g. ["email", "role"] - empty if a validator failed instead
error.validationErrors; // e.g. ["Age must be 18 or older"] - empty if fields were missing
}
}The instanceof check is also what makes this compile under useUnknownInCatchVariables.
| Method | Compile-Time Check | Runtime Check | Use Case |
|---|---|---|---|
build() |
β | β | Production code |
buildWithoutRuntimeValidation() |
β | β | Performance-critical |
buildWithoutCompileTimeValidation() |
β | β | External/dynamic data |
buildUnsafe() |
β | β | Trusted scenarios only |
buildPartial() |
β | β | Incomplete objects |
When a compile-time-checked variant is called too early, the error ends with the properties that are still missing:
The 'this' context of type 'InternalBuilderStep<UserBuilder, User, "id">' is not assignable to method's 'this' of type 'MissingRequiredProperties<"name" | "role">'.
Set the listed properties and the call compiles. If a property is listed because you removed it with removeRequiredProperty, see Seeding, Cloning, and Removing.
// Runtime-only validation is useful when building from external data:
function buildFromApi(data: Record<string, unknown>): User {
let builder = UserBuilder.create();
if (typeof data.id === "string") builder = builder.id(data.id);
// ...
return builder.buildWithoutCompileTimeValidation(); // throws CeriosBuilderError if incomplete
}Each validating build variant also has frozen and sealed versions:
| Method | Modify Top-Level | Modify Nested | Add/Delete Properties | Returns |
|---|---|---|---|---|
buildFrozen() |
β | β | β (top level) | Readonly<T> |
buildDeepFrozen() |
β | β | β (all levels) | DeepReadonly<T> |
buildSealed() |
β | β | β (top level) | T |
buildDeepSealed() |
β | β | β (all levels) | T |
Use deep frozen for configuration objects and snapshots, deep sealed for fixed schemas with mutable values, and the shallow variants when nested objects should stay flexible. All of them validate first and return a fresh copy β the builder itself is never frozen.
An auto setter always replaces a whole root property. Deep structures are composed from small builders, in two complementary ways.
To let callers build a nested object inline, add a method taking a callback typed with BuilderComposerFromFactory<> (ClassBuilderComposerFromFactory<> for classes). It infers the callback's input and its required output from the child factory: the callback gets a builder with the factory's defaults already counted as set, and it will not compile unless it returns a fully-set child builder.
import { BuilderComposerFromFactory, BuilderWith, CeriosAutoBuilder } from "@cerios/cerios-builder";
class CustomerBuilder extends CeriosAutoBuilder<Customer>() {
static create(): CustomerBuilder {
return new CustomerBuilder({});
}
withAddress(
fn: BuilderComposerFromFactory<typeof AddressBuilder.createWithDefaults>,
): BuilderWith<this, "address", CustomerBuilder> {
return this.address(fn(AddressBuilder.createWithDefaults()).build());
}
// Factory presets everything -> the callback can be optional
withCompleteAddress(
fn?: BuilderComposerFromFactory<typeof AddressBuilder.createComplete>,
): BuilderWith<this, "address", CustomerBuilder> {
const builder = AddressBuilder.createComplete();
return this.address(fn ? fn(builder).build() : builder.build());
}
}
const customer = CustomerBuilder.create()
.id("1")
.name("Alice")
.withAddress((address) => address.street("Main St").city("Rotterdam")) // `country` already defaulted
.build();
// Compile errors you get for free:
CustomerBuilder.create().withAddress((address) => address.street("Main St")); // city not set
CustomerBuilder.create().withAddress(() => ContactBuilder.create().email("a@b.c")); // wrong target typeGive the composer method a distinct name (withAddress) and let it delegate to the auto setter (this.address(...)) β that keeps the generated setter available to do the actual work.
For deep structures β where the deprecated builders used setNestedProperty dot-paths β give each level its own builder and put the assembly recipe in a director:
type OrderRequest = { order: { details?: OrderDetails } };
class AddressBuilder extends CeriosAutoBuilder<Address>() {
static create(): AddressBuilder {
return new AddressBuilder({});
}
static createDomestic(): BuilderWith<AddressBuilder, "country"> {
return AddressBuilder.create().country("USA");
}
}
class OrderDetailsBuilder extends CeriosAutoBuilder<OrderDetails>() {
static create(): OrderDetailsBuilder {
return new OrderDetailsBuilder({});
}
static createPending(): BuilderWith<OrderDetailsBuilder, "status"> {
return OrderDetailsBuilder.create().status("pending");
}
}
class OrderRequestBuilder extends CeriosAutoBuilder<OrderRequest>() {
static create(): OrderRequestBuilder {
return new OrderRequestBuilder({});
}
}
/** One place that knows how a complete OrderRequest is put together. */
class OrderRequestDirector {
static standardOrder(input: { customerId: string; totalAmount: number; street: string; city: string }): OrderRequest {
const shippingAddress = AddressBuilder.createDomestic().street(input.street).city(input.city).build();
const details = OrderDetailsBuilder.createPending()
.customerId(input.customerId)
.totalAmount(input.totalAmount)
.shippingAddress(shippingAddress)
.build();
return OrderRequestBuilder.create().order({ details }).build();
}
// Callers can customise one level without the director losing control of the recipe:
static orderShippedTo(
input: { customerId: string; totalAmount: number },
composeAddress: BuilderComposerFromFactory<typeof AddressBuilder.createDomestic>,
): OrderRequest {
const shippingAddress = composeAddress(AddressBuilder.createDomestic()).build();
// ... assemble as above
}
}
const order = OrderRequestDirector.standardOrder({
customerId: "CUST-001",
totalAmount: 299.99,
street: "123 Main St",
city: "New York",
});Why this beats dot-path writing into one big object:
- every level is compile-time checked by its own builder β a missing
cityis a compile error, not a runtime surprise; - each child builder is reusable on its own and in other directors;
- the director gives the recipe a name (
standardOrder) instead of scattering path strings.
The runnable version lives in tests/cerios-auto-builder/cerios-auto-builder-director.test.ts.
// Seed from an existing object or instance (deep-cloned; immediately buildable):
const updated = UserBuilder.from(existingUser).name("Renamed").build();
// Independent copy of a builder in progress:
const fork = builder.clone();
// Remove optional properties:
builder.removeOptionalProperty("age"); // one optional key
builder.clearOptionalProperties(); // all optional keys, required ones survive
// Remove a required property - e.g. to build an invalid object for a negative test:
const withoutName = UserBuilder.from(existingUser).removeRequiredProperty("name").buildUnsafe();Everything is copy-on-write: each call returns a new builder, and builders never share mutable state with each other or with the objects they build.
After removeRequiredProperty, the compile-time-checked variants (build(), buildFrozen(), β¦) no longer compile on that builder β their error lists the removed property. Use buildUnsafe() or buildPartial() to get the incomplete object. Setting the property again on that same builder does not lift this (the compile-time tracking cannot forget a removal); since builders are immutable, build from the builder you had before the removal instead.
Auto builders shine for test fixtures β defaults in factories, variation in the test:
class ProductBuilder extends CeriosAutoBuilder<Product>() {
static create(): ProductBuilder {
return new ProductBuilder({});
}
static createValid(): BuilderWith<ProductBuilder> {
return ProductBuilder.create().name("Widget").price(9.99).category("Tools");
}
asFree(): BuilderWith<this, "price", ProductBuilder> {
return this.price(0);
}
}
test("free products skip payment", () => {
const product = ProductBuilder.createValid().asFree().build();
expect(paymentRequired(product)).toBe(false);
});- Class data properties that are function-typed (e.g.
onChange: (x: number) => void) are treated like methods and get no setter β there's no reliable way to distinguish them from methods at the type level. - A custom method cannot have the same name as a property; use a distinct name that delegates to the generated setter (see Method names).
- Inside a generic base builder (e.g.
class Base<T extends Shape> extends ...), the compile-time gate cannot resolve untilTis concrete β the same rule as for hand-written builders. To share logic across derived types, use a base-builder function instead. readonlyobject properties keep their setters,readonlyclass fields do not. For a plain object type there is no constructor β the builder is the construction (the same reason an object literal may initialize areadonly id), soCeriosAutoBuildergenerates a working setter and the built object staysreadonly-typed. A class owns itsreadonlyfields through its own constructor, soCeriosClassAutoBuildergenerates no setter for them and the build gate doesn't demand them β seed them withnew Builder({ id: "1" })orBuilder.from(instance)instead, or enforce a genuinely required one with runtimerequiredFields.- Getter-only class accessors (
get displayName() { ... }) are computed by the class. Because a getter without a setter isreadonlyat the type level, no setter is generated β the misuse is a compile error. A runtime guard also throws a clearCeriosBuilderErrornaming the accessor, for untyped access (types are erased at runtime, so the proxy cannot seereadonly) and for seed data. Accessors with both a getter and a setter are writable and get a setter as normal. - Symbol-keyed properties are outside the builder's model: setters are generated for string keys only, and state snapshots deep-clone via string keys, so a symbol-keyed value doesn't survive a build. If a required property is symbol-keyed, the compile gate cannot be satisfied β seed via
from()or usebuildUnsafe(). - Builder subclass fields are carried by reference across the copy-on-write forks every setter creates: a
Mapor array field on your builder class is shared between all forks and clones. Only the target data is deep-cloned β keep per-fork state in the target data, not in builder fields. Prop-suffixed setters (for properties named after a builder member, likebuildProp) are not linked to the property declaration, so Ctrl+Click on them does not open the property. All other setters are linked.
The hand-written builder base classes β where you write a setProperty wrapper per property β are deprecated and will be removed in the next major version. They still work in this release, including setNestedProperty and addToArrayProperty, and share the auto builders' build error and removeRequiredProperty.
Migrate to the auto builders: see MIGRATION.md for a per-feature before/after guide, including:
- dropping setter wrappers and constructor plumbing,
- moving
static requiredTemplate/requiredDataPropertiesto constructor options, - replacing
setNestedPropertywith the director pattern, - replacing
addToArrayPropertywith a custom method on the root setter.
The helper types that only made sense for the hand-written builders, such as BuilderStep, BuilderPreset, BuilderComposer, BuildGate and RequiredFieldsTemplate, are deprecated too. They are still exported. MIGRATION.md Β§ Deprecated helper types lists the replacement for each one.
CeriosAutoBuilder<T>()β returns an abstract base class with an auto setter per property ofT.CeriosClassAutoBuilder(Class)β the same for a class; building instantiatesClass. Returns the same (memoised) base class for the sameClass, soinstanceofworks across calls.
Both auto builders share one constructor shape, with the options named by the exported BuilderInit type:
new Builder(data?: Partial<T>, init?: BuilderInit<T, P, Data>)
type BuilderInit<T, P, Data = T> = {
requiredFields?: ReadonlyArray<P> | RequiredFieldsRecord<Data>;
validators?: Array<(obj: Partial<T>) => boolean | string>;
};To name the options yourself, use BuilderInit<User, Path<User>> for CeriosAutoBuilder<User>() and BuilderInit<Person, ClassPath<Person>, DataPropertiesOnly<Person>> for CeriosClassAutoBuilder(Person). The class form takes data properties only, never methods.
build(), buildWithoutRuntimeValidation(), buildWithoutCompileTimeValidation(), buildUnsafe(), buildPartial(), buildFrozen(), buildDeepFrozen(), buildSealed(), buildDeepSealed(), addValidator(), setRequiredFields(), removeOptionalProperty(), removeRequiredProperty(), clearOptionalProperties(), clone() β see the sections above.
Builder.from(instance)β seeds a new builder from an existing object/instance (deep-cloned), immediately buildable.
BuilderWith<B, S, KeysFrom>/ClassBuilderWith<B, S, KeysFrom>β explicit return types for factories and custom methods;Bis the builder returned (thisin instance methods);Sis the union of keys set, omit for "fully buildable";KeysFromis the builder class the keys are checked against and suggested from (defaults toB, pass the class whenBisthis).MissingRequiredProperties<K>β the type the build error names when the required propertiesKare not set.RemovedRequiredProperties<K>β the markerremoveRequiredPropertyadds; it keeps the compile-time-checked build variants unavailable.AutoBuilderBase<TBase>/ClassAutoBuilderBase<TBase>β constraint for base-builder functions sharing logic across derived types.BuilderExtension<TBuilder, TShared>β return type of a base-builder function; keeps the derived builder's setters and statics visible.BuilderComposerFromFactory<F>/ClassBuilderComposerFromFactory<F>β callback types for nested-builder composition, inferred from a factory.BuilderInit<T, P, Data>β the named{ requiredFields?, validators? }constructor-options type both auto builders accept:BuilderInit<T, Path<T>>for object builders,BuilderInit<T, ClassPath<T>, DataPropertiesOnly<T>>for class builders.AutoBuilderConstructor<T>/ClassAutoBuilderConstructor<T>β the abstract constructor type a factory returns; name it when storing or passing a factory result.AutoBuilderApi<T>/ClassAutoBuilderApi<T>β the build/validate/clone instance API every auto builder exposes, without the generated setters.AutoSetters<T>/ClassAutoSetters<T>β the generated bare-name setters (one per property; the class variant covers writable data properties only β methods, getter-only accessors, andreadonlyfields are excluded).DataPropertiesOnly<T>β a class type with its methods stripped; the shape the class builders track and build from.ClassConstructor<T>β the constructor signatureCeriosClassAutoBuilder(Class)accepts for a target class.InternalBuilderStep/InternalClassBuilderStepβ the type every generated setter returns. It shows up in hovers and in the build error; don't write it yourself, useBuilderWith/ClassBuilderWith.RequiredFieldsRecord<T>,Path<T>,ClassPath<T>,DeepReadonly<T>,OptionalKeys<T>,RequiredKeys<T>,WritableKeys<T>.
Deprecated, still exported (see MIGRATION.md for before/after):
BuilderStep/ClassBuilderStep,BuilderPreset/ClassBuilderPresetβBuilderWith/ClassBuilderWithBuilderComposer/ClassBuilderComposerβBuilderComposerFromFactory/ClassBuilderComposerFromFactoryBuildGate/ClassBuildGateβBuilderWith<YourBuilder>/ClassBuilderWith<YourBuilder>RequiredFieldsTemplate<T>βRequiredFieldsRecord<T>, orReadonlyArray<Path<T>>for nested pathsCeriosBrand/CeriosClassBrand,BuilderTypeβBuilderWith/BuilderComposerFromFactory
Contributions are welcome! Please feel free to submit a Pull Request.
MIT Β© Ronald Veth - Cerios