Compose.intersection
Declaration. Creates a new allOf schema that combines multiple schemas. Data must satisfy every constituent schema simultaneously. TypeScript infers the intersection of all constituent types. The $id is set to newId.
Use this when data must satisfy multiple independent schemas - for example, an AuditedOrder must satisfy both Order constraints and Audit constraints including their respective required arrays. This is stronger than extend, which only merges properties into one flat object.
Don't use this when you only need to merge property definitions without additional required constraints (use extend - simpler, more predictable). Don't use it for union types (use discriminatedUnion).
Examples
Example 1: Add audit fields to Order
Both Order and Audit required arrays must be satisfied.
import { Compose, JsonTology } from 'json-tology';
import type { InferType } from 'json-tology/types';
import { OrderLineSchema, OrderSchema } from './bookstore/index.js';
const AuditSchema = {
$id: 'https://bookstore.example/Audit',
type: 'object',
properties: {
createdAt: { type: 'string', format: 'date-time' },
updatedAt: { type: 'string', format: 'date-time' },
createdBy: { type: 'string' },
},
required: ['createdAt', 'updatedAt'],
} as const;
const AuditedOrderSchema = Compose.intersection(
[OrderSchema, AuditSchema] as const,
'https://bookstore.example/AuditedOrder',
);
type AuditedOrder = InferType<typeof AuditedOrderSchema>;
// Order & { createdAt: string; updatedAt: string; createdBy?: string }
const jt = JsonTology.create({
baseIRI: 'https://bookstore.example',
schemas: [OrderLineSchema, OrderSchema, AuditSchema, AuditedOrderSchema] as const,
});Example 2: Validation fails if any constituent schema fails
// Missing createdAt and updatedAt from AuditSchema required:
const errors = jt.validate(AuditedOrderSchema.$id, {
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
customerId: 'c1a2b3d4-e5f6-7890-abcd-ef1234567890',
placedAt: '2026-01-15T10:30:00Z',
total: 14.99,
items: [{ bookIsbn: '9780140449136', quantity: 1, unitPrice: 14.99 }],
// createdAt and updatedAt missing
});
console.log(errors.length > 0); // trueExample 3: getDefaults on an intersection schema
Build on Compose.getDefaults - extracting defaults from an intersection walks each constituent.
import { Compose } from 'json-tology';
const defaults = Compose.getDefaults(OrderSchema);
// { currency: 'USD' }
// Combined schemas' defaults are merged at validation timeBad examples - what NOT to do
Anti-pattern 1: Using intersection when extend is simpler
import { Compose } from 'json-tology';
// ⊥ Don't do this for simple property merging - allOf is heavier than needed
const ExtendedSchema = Compose.intersection(
[BookSchema, { type: 'object', properties: { badge: { type: 'string' } } }] as const,
'https://bookstore.example/ExtendedBook',
);
// ✓ Do this - extend is designed for this
const ExtendedSchema2 = Compose.extend(
BookSchema,
{ badge: { type: 'string' } } as const,
'https://bookstore.example/ExtendedBook',
);Comparison
Compose.intersection([OrderSchema, AuditSchema] as const, 'https://bookstore.example/AuditedOrder')
// Produces allOf: [OrderSchema, AuditSchema]
// Both required arrays must be satisfiedOrderSchema.and(AuditSchema)
// Or: z.intersection(OrderSchema, AuditSchema)import * as v from 'valibot';
v.intersect([OrderSchema, AuditSchema])import { Type } from '@sinclair/typebox';
Type.Intersect([OrderSchema, AuditSchema])const AuditedOrderSchema = {
$id: 'https://bookstore.example/AuditedOrder',
allOf: [OrderSchema, AuditSchema],
};class AuditedOrder(Order, Audit):
# Multiple inheritance achieves allOf semantics
passRelated
extend- simpler for just adding properties without separate required constraintsdiscriminatedUnion- for oneOf with type discriminatorpartial- make the intersected result partially optional
See also
- Bookstore domain - where
OrderSchemais defined - Composition index - overview of all composition operations