Skip to content

Compose.partial and Compose.required

partial and required are inverse operations for adjusting the required-ness of all properties. Both return new schema objects - inputs are never mutated.


Compose.partial

Declaration. Creates a new schema by removing the required array entirely. All properties become optional. TypeScript infers a type with all properties optional - the equivalent of Partial<T>. The $id is replaced with newId.

Use this when you need a PATCH-body schema where any combination of fields may be provided. Also useful for form state where all fields start empty and become required only on submit.

Don't use this when you only want specific fields to be optional (use pick to select and then omit required from only those). Don't use it when you want to remove specific fields entirely (use omit).

Examples

Example 1: PATCH customer endpoint

ts
import { Compose, JsonTology } from 'json-tology';
import type { InferType } from 'json-tology/types';
import { CustomerSchema } from './bookstore/index.js';

const PatchCustomerSchema = Compose.partial(
  CustomerSchema,
  'https://bookstore.example/PatchCustomer',
);

type PatchCustomer = InferType<typeof PatchCustomerSchema>;
// { id?: string; email?: string; name?: string; addresses?: Address[] }

const jt = JsonTology.create({
  baseIRI: 'https://bookstore.example',
  schemas: [PatchCustomerSchema] as const,
});

// PATCH body  - only name provided
const patch = jt.instantiate(PatchCustomerSchema.$id, { name: 'Alice P. Chen' });
// { name: 'Alice P. Chen' }

Example 2: Form initial state for a Review

ts
import { Compose, JsonTology } from 'json-tology';
import { ReviewSchema } from './bookstore/index.js';

const DraftReviewSchema = Compose.partial(
  ReviewSchema,
  'https://bookstore.example/DraftReview',
);

// Valid even with nothing filled in  - all optional
const jt2 = JsonTology.create({
  baseIRI: 'https://bookstore.example',
  schemas: [DraftReviewSchema] as const,
});
const errors = jt2.validate(DraftReviewSchema.$id, {});
console.log(errors.length === 0); // true

Comparison

ts
Compose.partial(CustomerSchema, 'https://bookstore.example/PatchCustomer')
ts
CustomerSchema.partial()
ts
import * as v from 'valibot';
v.partial(CustomerSchema)
ts
import { Type } from '@sinclair/typebox';
Type.Partial(CustomerSchema)
ts
// Manual  - copy schema, remove required:
const { required: _, ...PatchCustomer } = CustomerSchema;
PatchCustomer.$id = 'https://bookstore.example/PatchCustomer';
py
# Create a PATCH model manually with Optional fields:
class PatchCustomer(BaseModel):
    name: str | None = None
    email: str | None = None
    # Or use model_fields_set to track which fields were provided.
  • required - inverse: make all fields required
  • pick - subset of fields, combined with partial for partial sub-schemas
  • extend - add fields before making partial

Compose.required

Declaration. Creates a new schema where every declared property in properties is listed in required. The resulting required array is Object.keys(schema.properties). TypeScript infers a type with all properties required - the equivalent of Required<T>. The $id is replaced with newId.

Use this when you need a strict create-body schema that demands all fields, even those that have defaults. Useful for internal service calls where missing defaults should be caught, or for admin APIs that require full objects.

Don't use this when you only want specific fields to be required (build a combined schema with intersection instead).

Examples

Example 1: Strict book creation requiring all fields

BookSchema has currency and inStock with defaults (so they're effectively optional in the base schema). A strict create schema requires them explicitly.

ts
import { Compose, JsonTology } from 'json-tology';
import type { InferType } from 'json-tology/types';
import { BookSchema } from './bookstore/index.js';

const CreateBookSchema = Compose.required(
  BookSchema,
  'https://bookstore.example/CreateBook',
);

type CreateBook = InferType<typeof CreateBookSchema>;
// { isbn: string; title: string; authors: string[]; price: number; currency: string; inStock: boolean }
// ALL fields required

const jt = JsonTology.create({
  baseIRI: 'https://bookstore.example',
  schemas: [CreateBookSchema] as const,
});

// Missing currency and inStock
const errors = jt.validate(CreateBookSchema.$id, {
  isbn:    '9780140449136',
  title:   'Crime and Punishment',
  authors: ['Fyodor Dostoevsky'],
  price:   14.99,
});
console.log(errors.length > 0); // true  - currency and inStock are now required

Comparison

ts
Compose.required(BookSchema, 'https://bookstore.example/CreateBook')
ts
BookSchema.required()
ts
import * as v from 'valibot';
v.required(BookSchema)
ts
import { Type } from '@sinclair/typebox';
Type.Required(BookSchema)
ts
// Manual  - set required = all property keys:
const CreateBook = {
  ...BookSchema,
  $id: 'https://bookstore.example/CreateBook',
  required: Object.keys(BookSchema.properties),
};
py
# Pydantic fields without defaults are already required.
# To make defaulted fields required, remove the defaults or use a validator.
  • partial - inverse: make all fields optional
  • intersection - when some fields must be required from a second schema
  • extend - add fields before making required

See also

Released under the MIT License.