Ajv supports json-schema draft-7 (2020-12), and I think it would be better to rewrite it, since it is more convenient and flexible
And I think that the methods should not modify the scheme, but should return a new scheme, as is done in other validators
import { s } from 'ajv-ts';
import { assertEquals } from 'https://deno.land/[email protected]/assert/mod.ts';
Deno.test('from JSON', async t => {
const sexJsonSchema = {
type: 'string',
enum: ['male', 'female'],
title: 'Sex',
};
const nullableSexJsonSchema = {
anyOf: [sexJsonSchema, { type: 'null' }],
};
const sex = s.fromJSON(sexJsonSchema);
const nullableSex = s.fromJSON(nullableSexJsonSchema);
const optionalNullableObj = s.fromJSON({
type: 'object',
properties: {
sex: nullableSexJsonSchema,
},
});
const requiredNullableObj = s.fromJSON({
type: 'object',
properties: {
sex: nullableSexJsonSchema,
},
required: ['sex'],
});
await t.step('enum', () => {
assertEquals(sex.parse('male'), 'male');
assertEquals(sex.parse('female'), 'female');
assertEquals(sex.validate(null), false);
assertEquals(sex.validate(undefined), false);
});
await t.step('nullable enum', () => {
assertEquals(nullableSex.parse('male'), 'male');
assertEquals(nullableSex.parse('female'), 'female');
assertEquals(nullableSex.validate(null), true);
assertEquals(nullableSex.validate(undefined), false);
});
await t.step('optionalNullableObj', () => {
assertEquals(optionalNullableObj.validate({}), true);
assertEquals(optionalNullableObj.validate({ sex: null }), true);
assertEquals(optionalNullableObj.validate({ sex: 'male' }), true);
});
await t.step('requiredNullableObj', () => {
assertEquals(requiredNullableObj.validate({}), false);
assertEquals(requiredNullableObj.validate({ sex: null }), true);
assertEquals(requiredNullableObj.validate({ sex: 'male' }), true);
});
});
Deno.test('from ajv-ts', async t => {
const sex = s.enum(['male', 'female']);
const nullableSex = s.enum(['male', 'female']).nullable();
const optionalNullableObj = s.object({
sex: s.enum(['male', 'female']).nullable().optional(),
});
await t.step('enum', () => {
assertEquals(sex.parse('male'), 'male');
assertEquals(sex.parse('female'), 'female');
assertEquals(sex.validate(null), false);
assertEquals(sex.validate(undefined), false);
});
// fail
await t.step('nullable enum', () => {
assertEquals(nullableSex.parse('male'), 'male');
assertEquals(nullableSex.parse('female'), 'female');
assertEquals(nullableSex.validate(null), true);
assertEquals(nullableSex.validate(undefined), false);
});
// fail
await t.step('optionalNullableObj', () => {
assertEquals(optionalNullableObj.parse({}), {});
assertEquals(optionalNullableObj.parse({ sex: 'male' }), { sex: 'male' });
assertEquals(optionalNullableObj.parse({ sex: null }), { sex: null });
});
});
const sexJsonSchema = {
type: 'string',
enum: ['male', 'female'],
title: 'Sex',
};
const nullableSexJsonSchema = {
anyOf: [sexJsonSchema, { type: 'null' }],
};
// Required nullable enum
{
const a = s.fromJSON({
type: 'object',
properties: {
sex: nullableSexJsonSchema,
},
required: ['sex'],
});
const b = s.object({
sex: s.enum(['male', 'female']).nullable(),
});
console.log(a.validate({ sex: null })); // true
console.log(a.validate({ sex: 'male' })); // true
console.log(b.validate({ sex: null })); // false
console.log(b.validate({ sex: 'male' })); // false
console.log(a.schema, b.schema);
}
// Optional nullable enum
{
const a = s.fromJSON({
type: 'object',
properties: {
sex: nullableSexJsonSchema,
},
});
const b = s.object({
sex: s.enum(['male', 'female']).nullable().optional(),
});
console.log(a.validate({})); // true
console.log(a.validate({ sex: null })); // true
console.log(b.validate({})); // false
console.log(b.validate({ sex: null })); // false
console.log(a.schema, b.schema);
}