object-shape-tester
Version:
Test object properties and value types.
59 lines (58 loc) • 2.04 kB
JavaScript
import { mapObjectValues } from '@augment-vir/common';
import { Type, } from '@sinclair/typebox';
import { defineShape } from '../shape/shape.js';
import { canSchemaBeNullable, insertUnion, isObjectSchema, isUndefinedSchema, matchesSchemaGuard, operateOnExtraction, } from '../util/typebox-util.js';
/**
* Creates a shape from an object shape where any property that can be any kind of nullable
* (optional, `null`, or `undefined`) can now be _all_ kinds of nullable. That is, any property that
* is optional or can be `null` or `undefined` becomes all three.
*
* @category Shape
* @example
*
* ```ts
* import {
* nullToNullableShape,
* unionShape,
* assertValidShape,
* defineShape,
* } from 'object-shape-tester';
*
* const myShape = nullToNullableShape({
* a: '',
* b: unionShape(null, -1),
* c: null,
* d: {
* e: unionShape(null, ''),
* },
* });
*
* // passes
* assertValidShape({a: 'hi', b: null, d: {}}, myShape);
* assertValidShape({a: 'hi', b: undefined, d: {e: undefined}}, myShape);
*
* // fails
* assertValidShape({b: null}, myShape);
* assertValidShape({a: 'hi'}, myShape);
* ```
*/
export function ensureNullableShape(originalShape) {
const shape = defineShape(originalShape);
return defineShape(operateOnExtraction(shape.$_schema, isObjectSchema, (schema) => {
const newProperties = mapObjectValues(schema.properties, (propKey, propSchema) => {
const mappedPropSchema = ensureNullableShape(propSchema).$_schema;
if (!canSchemaBeNullable(mappedPropSchema)) {
return mappedPropSchema;
}
return Type.Optional(matchesSchemaGuard(mappedPropSchema, isUndefinedSchema)
? mappedPropSchema
: insertUnion({
originalSchema: mappedPropSchema,
insertion: Type.Undefined(),
}));
});
return Type.Object(newProperties, {
default: shape.default,
});
}));
}