Compose.getDefaults
Declaration. Extracts declared default values from a schema's properties, returning a plain object containing only the properties that have a default keyword declared. Properties without defaults are omitted. Nested object properties with their own default-bearing children are recursively traversed.
Use this when you want to pre-populate form fields with schema defaults before the user has provided any input - without running a full coerce that would fail on missing required fields. This is the lightweight alternative to jt.materialize(schema) when you only want explicit defaults and not zero-value synthesis.
Don't use this when you want zero-values for all required fields (use jt.value.create). Don't use it when you want a full materialized instance (use jt.materialize).
Examples
Example 1: Pre-populate a new Book form
BookSchema has currency: 'USD' and inStock: true. isbn, title, authors, and price have no declared defaults - they are omitted from the result.
import { Compose } from 'json-tology';
import { BookSchema } from './bookstore/index.js';
const defaults = Compose.getDefaults(BookSchema);
console.log(defaults);
// { currency: 'USD', inStock: true }
// Initialize form state with defaults pre-filled, others empty:
const formState = {
...defaults,
isbn: '',
title: '',
authors: [],
price: 0,
};Example 2: Pre-populate an Order form
import { Compose } from 'json-tology';
import { OrderSchema } from './bookstore/index.js';
const defaults = Compose.getDefaults(OrderSchema);
console.log(defaults);
// { currency: 'USD' }
// id, customerId, items, total, placedAt have no defaults - absent from resultExample 3: Nested defaults are traversed
import { Compose } from 'json-tology';
const SettingsSchema = {
$id: 'https://bookstore.example/Settings',
type: 'object',
properties: {
theme: { type: 'string', default: 'light' },
notifications: {
type: 'object',
properties: {
email: { type: 'boolean', default: true },
push: { type: 'boolean', default: false },
},
},
},
} as const;
const defaults = Compose.getDefaults(SettingsSchema);
// { theme: 'light', notifications: { email: true, push: false } }Bad examples - what NOT to do
Anti-pattern 1: Using getDefaults as a substitute for coerce/materialize
// ⊥ Don't do this - getDefaults returns only declared defaults, not all fields
const partial = Compose.getDefaults(CustomerSchema);
// partial === {} - Customer has no declared defaults (addresses has default [])
// This is NOT a valid Customer object
// ✓ Do this for a blank Customer form:
const blank = jt.value.create(CustomerSchema.$id);
// → { id: '', email: '', name: '', addresses: [] }Comparison
Compose.getDefaults(BookSchema)
// { currency: 'USD', inStock: true }
// Only properties with declared `default` values// Zod has no built-in getDefaults. Closest workaround:
// schema.parse({}) fails on missing required fields.
// Manual extraction hardcodes default values:
const defaults = { currency: 'USD', inStock: true };
// Limitation: hardcoded, not derived from the schema. Must be updated manually
// every time a default changes. No recursive traversal of nested objects.import * as v from 'valibot';
// Limitation: Valibot has no exposed default-extraction helper.
// Defaults attached via v.optional(schema, defaultValue) are applied
// during v.parse but are not enumerable by a public API.
const defaults = { currency: 'USD', inStock: true }; // hardcodedimport { Value } from '@sinclair/typebox/value';
// Value.Default fills ALL fields including zero-values, not just declared defaults:
Value.Default(BookSchema, {})
// → { isbn: undefined, title: undefined, ..., currency: 'USD', inStock: true }
// More fields than getDefaults// AJV has no built-in getDefaults utility.
// Closest workaround: validate an empty object with { useDefaults: true }:
const ajv = new Ajv({ useDefaults: true });
const draft = {};
ajv.validate(bookSchema, draft);
// draft now has defaults injected into it.
// Limitation: modifies the input object in place; requires a full validate call;
// does not return only the declared defaults - fills them into an existing object.# Extract defaults from model fields:
from pydantic._internal._fields import PydanticUndefined
defaults = {
name: field.default
for name, field in Book.model_fields.items()
if field.default is not PydanticUndefined
}
# { 'currency': 'USD', 'in_stock': True }Related
jt.materialize- build a full instance from partial data + defaultsjt.value.create- synthesize zero-values for all required fields + explicit defaults
See also
- Bookstore domain -
BookSchemaandOrderSchemadefaults - Composition index - overview of all composition operations