Composition
allOf — schema intersection
All schemas must validate (AND logic).
const strictStringSchema = new SchemaBuilder()
.allOf(
(s) => s.string().minLength(5),
(s) => s.string().maxLength(10),
(s) => s.string().pattern("^[A-Z]"),
)
.build();
// Must be 5–10 chars AND start with uppercase
// Valid: "Hello", "World" Invalid: "hi", "hello"Combining a base with an extension:
const baseUserSchema = new SchemaBuilder()
.object()
.properties({
id: (s) => s.integer(),
name: (s) => s.string(),
})
.required(["id", "name"]);
const adminUserSchema = new SchemaBuilder()
.allOf(
(s) => s.extend(baseUserSchema), // include all base properties
(s) =>
s
.object()
.properties({
permissions: (s) => s.array().items((s) => s.string()),
adminLevel: (s) => s.integer().minimum(1),
})
.required(["permissions"]),
)
.build();anyOf — schema union
At least one schema must validate (OR logic).
const flexibleIdSchema = new SchemaBuilder()
.anyOf(
(s) => s.string().pattern("^[A-Z]{3}\\d{3}$"), // e.g. "ABC123"
(s) => s.integer().minimum(1000), // e.g. 1234
)
.build();
// Valid: "ABC123" or 5000 Invalid: "abc123", 500A payment-method union:
const paymentMethodSchema = new SchemaBuilder()
.object()
.properties({ type: (s) => s.string() })
.anyOf(
(s) =>
s
.object()
.properties({
type: (s) => s.const("credit_card"),
cardNumber: (s) => s.string(),
cvv: (s) => s.string(),
})
.required(["type", "cardNumber", "cvv"]),
(s) =>
s
.object()
.properties({
type: (s) => s.const("bank_transfer"),
accountNumber: (s) => s.string(),
routingNumber: (s) => s.string(),
})
.required(["type", "accountNumber", "routingNumber"]),
(s) =>
s
.object()
.properties({
type: (s) => s.const("paypal"),
email: (s) => s.string().format("email"),
})
.required(["type", "email"]),
)
.build();oneOf — exclusive schema union
Exactly one schema must validate (XOR logic).
const stringLengthSchema = new SchemaBuilder()
.oneOf(
(s) => s.string().maxLength(10), // short string
(s) => s.string().minLength(50), // long string
)
.build();
// Valid: "hello", "this is a very long string..."
// Invalid: "medium length string" (matches neither or both)A discriminated union:
const shapeSchema = new SchemaBuilder()
.oneOf(
(s) =>
s
.object()
.properties({
type: (s) => s.const("circle"),
radius: (s) => s.number().minimum(0),
})
.required(["type", "radius"]),
(s) =>
s
.object()
.properties({
type: (s) => s.const("rectangle"),
width: (s) => s.number().minimum(0),
height: (s) => s.number().minimum(0),
})
.required(["type", "width", "height"]),
(s) =>
s
.object()
.properties({
type: (s) => s.const("triangle"),
base: (s) => s.number().minimum(0),
height: (s) => s.number().minimum(0),
})
.required(["type", "base", "height"]),
)
.build();
// Valid: { type: "circle", radius: 5 }
// Invalid: { type: "circle", radius: 5, width: 10 } (matches multiple)not — schema negation
The schema must NOT validate.
const notStringSchema = new SchemaBuilder().not((s) => s.string()).build();
// Valid: 123, true, {}, [] Invalid: "hello"An exclusion pattern:
// Accept any string EXCEPT email addresses
const nonEmailStringSchema = new SchemaBuilder()
.string()
.not((s) => s.string().format("email"))
.build();
// Valid: "hello", "test123" Invalid: "user@example.com"Complex composition
const advancedSchema = new SchemaBuilder()
.object()
.properties({ value: (s) => s.string() })
.allOf((s) => s.object().required(["value"]))
.anyOf(
(s) => s.object().properties({ type: (s) => s.string() }).required(["type"]),
(s) =>
s.object().properties({ category: (s) => s.string() }).required(["category"]),
)
.not((s) => s.object().required(["type", "category"]))
.build();
// Valid: { value: "x", type: "a" } or { value: "x", category: "b" }
// Invalid: { value: "x", type: "a", category: "b" } (has both)Last updated on