Reputation:
I'm using ajv with TypeScript and have a custom type MyCustomType
. When creating a validation schema I want to ensure that a specific property is of type MyCustomType
. So ajv should validate its structure and decide whether this could be parsed to the given type.
I started with the following sample code ( and created a Codesandbox example for testing purposes )
import { AType } from "./AType"; // custom type
import { AnInterface } from "./AnInterface"; // custom interface
import Ajv from "ajv";
type MyComplexType = AType | AnInterface; // create a more complex type from the given imports
const ajvInstance = new Ajv();
const schema = {
type: "object",
properties: {
myComplexType: { type: "string" } // value structure must match structure of MyComplexType
},
required: ["myComplexType"],
additionalProperties: false
};
const validate = ajvInstance.compile(schema);
const data = {
myComplexType: {}
};
const isValid = validate(data);
if (isValid) {
console.info("everything is fine");
} else {
validate.errors?.forEach((error) => console.error(error.message));
}
Currently I don't know how to create a validation schema for the property myComplexType
. I found some discussions
but I don't think these will help because
typeof
just returns "object"
instanceof
won't work for typesSo do I have to create a custom keyword ( as described here ) and write my own validation logic ( inspect the object structure ) or are there any things I can already use? How should I configure myComplexType
?
Upvotes: 3
Views: 2262
Reputation: 626
I flattened your structure to be able to answer with one code segment. This is TypeScript, and runs on NODE JS successfully using Ajv.
The key is using the JSONSchemaType from Ajv to bride between Ajv and TypeScript. If the TYPE and AJV definitions dont match up, TypeScript will complain. This is great, especially if your editor has a TypeScript real-time checker built in (VS Code, VIM, ...)
Notice that the schema MyComplexTypeSchema is defined using the previously defined schemas. It does not have to be one huge nested schema. Have a schema for every TYPE and INTERFACE, and build your library from the ground up to support both TypeScript and Ajv.
'use strict'
import Ajv, {JSONSchemaType} from "ajv"
const ajvInstance = new Ajv();
type AType = "x" | "y";
const ATypeSchema : JSONSchemaType<AType> = {
type:"string",
enum:["x","y"]
}
interface AnInterface {
prop: string;
}
const AnInterfaceSchema : JSONSchemaType<AnInterface> = {
type:"object",
properties:{
prop:{type:"string"}
},
required:["prop"]
}
type MyComplexType = AType | AnInterface;
const MyComplexTypeSchema : JSONSchemaType<MyComplexType> = {
oneOf:[ ATypeSchema,AnInterfaceSchema ]
}
const schema = {
type: "object",
properties: {
myComplexType: MyComplexTypeSchema
},
required: ["myComplexType"],
additionalProperties: false
};
const validate = ajvInstance.compile(schema);
const data1 = {
myComplexType: {
prop:"a string"
}
};
var isValid = validate(data1);
if (isValid) {
console.info("everything is fine in data1");
} else {
validate.errors?.forEach((error) => console.error(error.message));
}
const data2 = {
myComplexType: "x"
};
var isValid = validate(data2);
if (isValid) {
console.info("everything is fine in data2");
} else {
validate.errors?.forEach((error) => console.error(error.message));
}
const bad1 = {
myComplexType: {}
};
var isValid = validate(bad1);
if (isValid) {
console.info("everything is fine in bad1");
} else {
validate.errors?.forEach((error) => console.error("bad1: "+error.message));
}
const bad2 = {
myComplexType: "z"
};
var isValid = validate(bad2);
if (isValid) {
console.info("everything is fine in bad2");
} else {
validate.errors?.forEach((error) => console.error("bad2: "+error.message));
}
I left your "const schema" code alone, but it probably should be something like below, to be consistent with the other Type / Schema structuring.
type TopType = {
myComplexType : MyComplexType
}
const TopTypeSchema : JSONSchemaType<TopType> = {
type: "object",
properties: {
myComplexType: MyComplexTypeSchema
},
required: ["myComplexType"],
additionalProperties: false
};
const validate2 = ajvInstance.compile(TopTypeSchema);
// TypeScript will not let us build a BAD value if the type is defined
const data1:TopType = {
myComplexType: {
prop:"a string"
}
};
Upvotes: 1
Reputation: 1048
I don't think you can create a custom type
with this library, maybe you're confusing javascript types with typescript types. So you probably have 2 options: 1) instead of using types you use classes and use instanceof
for validating an instance of your class, e.g.
class MyClass {}
const instanceofDef = require("ajv-keywords/dist/definitions/instanceof")
instanceofDef.CONSTRUCTORS.MyClass = MyClass
ajv.validate({instanceof: "MyClass"}, new MyClass())
or 2) you create a nested schema like this
const schema = {
type: "object",
properties: {
myComplexType: {
type: "object",
properties: {
myProperty: { type: "string" },
myFunction: { type: "function" },
...
}
}
},
required: ["myComplexType"],
additionalProperties: false
};
const validate = ajvInstance.compile(schema);
const data = {
myComplexType: {
myProperty: "Hello World",
myFunction: function(){}
}
};
const isValid = validate(data);
Upvotes: 0