value.cast, value.clean, and value.convert
Schema-aware instance methods on jt.value. All three operate against the registry and require enableTypeCast: true in JsonTology.create options for type coercion to work.
value.cast
Declaration. Coerces types (e.g. "9.99" → 9.99, "true" → true) and applies schema default values. Requires enableTypeCast: true. Throws CoercionError when the coerced data fails validation.
Use this when ingesting data from sources that serialize numbers and booleans as strings - CSV imports, URL query parameters, HTML form submissions, application/x-www-form-urlencoded bodies.
Don't use this when the source data is already properly typed (use instantiate instead). Don't use cast when you want type coercion but not defaults (use convert).
Examples
Example 1: Cast form input with string numbers
import { JsonTology } from 'json-tology';
import { BookSchema } from './bookstore/index.js';
const jt = JsonTology.create({
baseIRI: 'https://bookstore.example',
schemas: [BookSchema] as const,
enableTypeCast: true,
});
const book = jt.value.cast(BookSchema.$id, {
isbn: '9780140449136',
title: 'Crime and Punishment',
authors: ['Fyodor Dostoevsky'],
price: '14.99', // string → coerced to number
inStock: 'true', // string → coerced to boolean
});
// { isbn: '...', ..., price: 14.99, inStock: true, currency: 'USD' }Example 2: Cast URL query params for a Review filter
// req.query.rating is '4' (string)
const params = jt.value.cast(ReviewSchema.$id, {
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
bookIsbn: '9780140449136',
customerId: 'c1a2b3d4-e5f6-7890-abcd-ef1234567890',
rating: req.query.rating, // '4' → 4
body: 'Absolutely gripping from start to finish.',
postedAt: new Date().toISOString(),
});
console.log(params.rating); // 4 (number)Comparison
const jt = JsonTology.create({ ..., enableTypeCast: true });
const book = jt.value.cast(BookSchema.$id, rawData); // strings coerced// Zod uses .instantiate() wrappers per-field:
const BookSchema = z.object({
price: z.coerce.number(),
inStock: z.coerce.boolean(),
});
const book = BookSchema.parse(rawData);import * as v from 'valibot';
// Limitation: no schema-wide cast option. Wrap each coerced field
// individually and rebuild the schema:
const BookSchema = v.object({
price: v.pipe(v.unknown(), v.transform(Number), v.number()),
inStock: v.pipe(v.unknown(), v.transform(Boolean), v.boolean()),
});
const book = v.parse(BookSchema, rawData);import { Value } from '@sinclair/typebox/value';
const book = Value.Convert(BookSchema, rawData); // type conversion// AJV has { coerceTypes: true } option:
const ajv = new Ajv({ coerceTypes: true });
ajv.validate(bookSchema, rawData); // rawData mutated in place# Pydantic v2 coerces compatible types by default (strict=False):
book = Book.model_validate(raw_data) # '14.99' → 14.99value.clean
Declaration. Strips properties not declared in the schema from the data. Throws CoercionError when the cleaned data fails validation.
Use this when you need to sanitize data that may carry extra properties not in the schema - for example, third-party API responses, database rows with extra columns, or enriched records that need to be reduced before forwarding.
Don't use this when you want defaults to be applied too (use instantiate which does both). Use clean when you specifically want only stripping, no defaults.
Examples
Example 1: Strip internal fields from an API response
const apiResponse = {
isbn: '9780140449136',
title: 'Crime and Punishment',
authors: ['Fyodor Dostoevsky'],
price: 14.99,
_internal_id: 'int-001', // not in BookSchema
_cache_key: 'k:9780140449136', // not in BookSchema
};
const cleaned = jt.value.clean(BookSchema.$id, apiResponse);
// { isbn: '...', title: '...', authors: [...], price: 14.99 }
// _internal_id and _cache_key are goneComparison
const cleaned = jt.value.clean(BookSchema.$id, data);
// Unknown properties stripped; validation error thrown if invalid// Zod's default .parse() strips unknown keys:
const cleaned = BookSchema.parse(data);import * as v from 'valibot';
// v.object() strips unknown keys by default during v.parse:
const cleaned = v.parse(BookSchema, data);
// Use v.looseObject() to preserve unknowns; v.strictObject() to reject them.import { Value } from '@sinclair/typebox/value';
Value.Clean(BookSchema, Value.Clone(data)); // removes additional properties// AJV with { removeAdditional: true }:
const ajv = new Ajv({ removeAdditional: true });
ajv.validate(bookSchema, data); // mutates data in place# Pydantic ignores extra fields by default (model_config extra='ignore'):
cleaned = Book.model_validate(data)value.convert
Declaration. Coerces types without applying schema default values. Requires enableTypeCast: true. Throws CoercionError when the data fails validation after type conversion.
Use this when you want type coercion but explicitly want to control which defaults are applied. Contrast: cast = coerce types + fill defaults; convert = coerce types only; instantiate = coerce types + fill defaults + strip unknowns + run transforms.
Examples
Example 1: Convert types for a partial review without filling defaults
const converted = jt.value.convert(ReviewSchema.$id, {
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
bookIsbn: '9780140449136',
customerId: 'c1a2b3d4-e5f6-7890-abcd-ef1234567890',
rating: '5', // coerced to number 5
body: 'One of the greatest novels ever written.',
postedAt: '2026-01-15T10:30:00Z',
});
console.log(converted.rating); // 5 (number)Related
JsonTology.instantiate- validate + apply defaults + strip unknowns + run transformsvalue.create- synthesize a zero-value blank instance
See also
- Bookstore domain - where
Book,Revieware defined