Yes. TypeBox conditional schemas exist for exactly this situation: data where the rules change depending on the values inside the data itself. A field that becomes required only when another field holds a certain value, a property that must appear when a status changes, or a record whose shape differs between account types can all be described in one schema.
None of this is a TypeBox invention. The conditions come from JSON Schema, and TypeBox exposes them as ordinary building blocks that still generate a TypeScript type. The honest answer does come with a boundary.
Simple and moderately complex conditions work well and stay readable for the people who maintain them. Very tangled business rules, where one requirement depends on three fields and a calculation, are usually better handled by a plain validation step that runs after the schema check. The rest of this article walks through what conditional requirements look like in practice, how TypeBox expresses them, and where the limits sit.
What a Conditional Data Requirement Looks Like in Practice
A conditional requirement is any rule you cannot state as a simple property of one field. Saying a name must be a string is unconditional.
Saying a company name must be present whenever the account type is set to business is conditional, because whether the rule applies depends on another value in the same object. That distinction sounds small, but it shows up in almost every real system.
Conditions in Checkout Forms
Checkout forms are a classic home for conditions. A delivery address is required when the customer chooses delivery, and pointless when they choose collection.
Payment details change shape depending on the payment method selected. A discount code field might be optional in general, yet need a matching reason field whenever it is used.
Conditions in APIs and Larger Systems
APIs produce the same pattern at a larger scale. A subscription object may carry renewal settings only for paid plans, while free plans must not send them at all.
A report request might allow a custom date range only when the report type is set to custom, and fixed ranges otherwise. In each case, the validity of one field is decided by the content of another, which is precisely what TypeBox is built to model: one definition that describes the data and produces the type your code works with.
The Negative Form of a Condition
Conditions also appear in a negative form. Sometimes a field is forbidden unless something else is true, such as an approval note that may only exist once a request has been reviewed. Expressing both directions, required-when and forbidden-unless, is what turns a loose object description into a real data contract.
The JSON Schema Patterns TypeBox Uses to Express Conditions
TypeBox conditional schemas rest on a small set of JSON Schema patterns rather than on any special TypeBox-only machinery. The best known is the if, then and else arrangement.
The schema first tests the data against a condition, much like a guard clause. When the data matches the condition, the rules in the then branch apply; when it does not, the else branch applies instead.
Dependent Properties
A second pattern covers dependent properties. Instead of testing values, it simply states that the presence of one property requires the presence of another.
This suits the company name example well, because the rule is about fields travelling together rather than about any particular value. For richer cases, schemas can also demand that a whole sub-schema holds whenever a flag field equals a given value.
Composition for Bigger Requirements
Composition does the heavy lifting for anything bigger. Conditions are combined with the same tools used everywhere else in JSON Schema: a value must match one of several complete shapes, or it must satisfy several partial schemas at once.
A common technique is to describe each variant of a record as its own complete shape and then say the data must match exactly one of them. The payment method example maps neatly onto this approach, with card, transfer and wallet each described in full.
Why Standard Patterns Travel Well
Because these are standard patterns, they travel well. A schema written for TypeScript validation can be shared with other tools that understand JSON Schema, and other developers reading it will recognise the conditional structure even if they have never used TypeBox. That portability is a practical advantage when schemas are shared between a frontend, a backend and partner systems.
How Conditions Affect the TypeScript Type You Get Back
The static type produced from a conditional schema is best thought of as an honest summary rather than a perfect mirror. When a record can take several shapes, the generated type becomes a union of those shapes. Your code then sees one type that could be the card variant or the transfer variant, and TypeScript pushes you to check which one you are holding before using variant-specific fields.
This works especially well when the variants are separated by a tag field, such as a method or kind property with a fixed set of values. TypeScript can narrow a union by that tag, so inside a branch that checks the tag, the correct fields become available and the others are excluded. Well-designed conditional schemas lean on this behaviour deliberately, because it makes the runtime rules and the static types tell the same story.
Where the Type Cannot Tell the Whole Story
Value-dependent rules are harder for the type system. A rule like “the company name is required when the account type is business” cannot be fully captured as a static type without contortions, because it ties the presence of one field to the runtime value of another.
The generated type will usually mark such fields as optional, and the schema carries the real enforcement at validation time. That split is normal and worth understanding: the type keeps your code convenient, while the schema remains the strict authority.
How Unlisted Data Interacts With Conditions
It also helps to know how unlisted data interacts with all of this. Strictness settings decide what extra properties do to a schema when incoming objects carry fields the schema never mentioned, and those settings apply whether or not the object also uses conditional rules.
How Validation Behaves When a Condition Is Triggered
At runtime, validation reads the actual data and follows the conditions wherever they lead. When the incoming object says the account type is business, the validator checks the branch that applies to businesses, including its extra required fields. When the type is personal, that branch is skipped entirely, and a missing company name is not treated as a problem.
This selective checking is the real payoff of TypeBox conditional schemas. One schema can accept several legitimate shapes while still rejecting broken ones, such as a business account with no company name or a delivery order with no address. Without conditions, teams usually end up with one loose schema plus scattered manual checks, and the manual checks are the part that quietly drifts out of date.
What Error Messages Look Like When a Branch Fails
Error reporting follows the branch that failed. If a condition is triggered and its required field is missing, the error points at the missing field within that branch.
In deeply nested conditions the messages can take some untangling, which is another reason to keep conditions as shallow as the data allows. A failing else branch can be particularly confusing to read, because the error describes a rule the reader may not have expected to apply.
Where Conditional Logic Becomes Too Complex for a Schema
Schemas are declarative, and that strength becomes a ceiling when the rule stops being about shape. A requirement that a total must equal the sum of its line items involves arithmetic across fields.
A rule that a coupon is valid only while a campaign is running depends on outside data and the current time. A rule that changes with the user’s history depends on state the schema never sees.
Nested Conditions as a Warning Sign
Nested conditions are the other warning sign. One level of branching is easy to read; three levels of if and else, combined with several alternative shapes, produces a schema nobody wants to review. At that point the schema is functioning as a small program written in a language with no debugging tools, and bugs hide in it comfortably.
The Two-Layer Approach
The practical pattern is a two-layer approach. Let the schema enforce structure and the conditions it expresses cleanly, then run ordinary code checks for the genuinely computational rules. TypeBox does not object to this division of labour; the schema still guards every boundary, and the code layer handles the handful of rules that were never really about shape.
Frequently Asked Questions
Can TypeBox require a field only when another field has a specific value?
Yes. That is the most common conditional requirement, and it is expressed with the standard if and then pattern: test the deciding field, and apply the extra requirement only when the test matches.
Do conditional schemas still produce a TypeScript type?
They do. Variant-based conditions produce union types that TypeScript can narrow by a tag field. Purely value-dependent requirements are enforced at validation time, with the affected fields usually appearing as optional in the static type.
Can conditions be nested inside other conditions?
They can, but deep nesting quickly becomes hard to read and hard to debug. If a rule needs several levels of branching, it is often a sign that part of the check belongs in ordinary code after validation.
Are TypeBox conditional schemas portable to other tools?
Mostly, yes. The conditions use standard JSON Schema patterns, so other validators that support those parts of the specification can read the same schema.
Conclusion
TypeBox conditional schemas comfortably represent the conditional requirements that appear in real forms, APIs and configuration data: fields that travel together, shapes that change with a tag value, and branches that apply only when a condition matches. The type you get back is a useful union or an approximation, while validation enforces the details. For rules involving arithmetic, outside data or deep branching, pair the schema with a small code check and you get the best of both layers.

Leave a Reply