Skip to Content
@jetio/schema-builder docs are live 🎉
Type InferenceConditionals

Conditionals

Conditional schemas infer as discriminated unions narrowed on the condition field. @jetio/schema-builder provides type inference for JSON Schema’s conditional keywords — including elseIf, which no other TypeScript schema library supports.

The type is built by merging the base properties, if, and then, then forming an exclusive-or with the merge of base and else — so only one branch can exist at a time. This extends through every elseIf.

Basic if / then / else

const addressSchema = new SchemaBuilder() .object() .properties({ country: (s) => s.string(), state: (s) => s.string(), postalCode: (s) => s.string(), }) .required(["country"]) .if((s) => s.object().properties({ country: (s) => s.const("US") })) .then((s) => s .object() .properties({ state: (s) => s.string(), zipCode: (s) => s.string().pattern("^[0-9]{5}$"), }) .required(["state"]), ) .else((s) => s.object().properties({ postalCode: (s) => s.string() }).required(["postalCode"]), ) .build(); type Address = Jet.Infer<typeof addressSchema>; // { // country: "US"; state: string; zipCode?: string; postalCode?: string; // } | { // country: string; postalCode: string; state?: string; zipCode?: undefined; // }

if / elseIf / else chains

const shippingSchema = new SchemaBuilder() .object() .properties({ country: (s) => s.string(), weight: (s) => s.number(), shippingMethod: (s) => s.string(), }) .required(["country", "weight"]) .if((s) => s.object().properties({ country: (s) => s.const("US") })) .then((s) => s .object() .properties({ shippingMethod: (s) => s.enum(["USPS", "UPS", "FedEx"]), estimatedDays: (s) => s.enum([2, 3, 5]), }) .required(["shippingMethod"]), ) .elseIf((s) => s.object().properties({ country: (s) => s.const("CA") })) .then((s) => s .object() .properties({ shippingMethod: (s) => s.enum(["Canada Post", "Purolator"]), estimatedDays: (s) => s.enum([5, 7, 10]), }) .required(["shippingMethod"]), ) .else((s) => s .object() .properties({ shippingMethod: (s) => s.string(), estimatedDays: (s) => s.number().minimum(14), }) .required(["shippingMethod"]), ) .build(); type Shipping = Jet.Infer<typeof shippingSchema>; // { // country: "US"; weight: number; // shippingMethod: "USPS" | "UPS" | "FedEx"; estimatedDays?: 2 | 3 | 5; // } | { // country: "CA"; weight: number; // shippingMethod: "Canada Post" | "Purolator"; estimatedDays?: 5 | 7 | 10; // } | { // country: string; weight: number; // shippingMethod: string; estimatedDays?: number; // }

Key behaviours: conditionals create discriminated unions narrowed on the condition field (one is enforced if absent); elseIf is evaluated in order, first match wins; each branch is exclusive — data matches exactly one path.

Last updated on