javascript - ajv - ตรวจสอบให้แน่ใจว่าโครงสร้างของวัตถุที่ให้มาตรงกับโครงสร้างของประเภทที่กำหนดเอง
ฉันกำลังใช้ ajv กับ TypeScript และมีชนิดข้อมูลที่กำหนดเอง MyCustomType เมื่อสร้างสคีมาสำหรับการตรวจสอบ ฉันต้องการให้แน่ใจว่าคุณสมบัติเฉพาะมีชนิดเป็น MyCustomType ดังนั้น ajv ควรตรวจสอบโครงสร้างของมันและตัดสินใจว่ามันสามารถถูกแปลงเป็นชนิดที่กำหนดได้หรือไม่
ฉันเริ่มด้วยโค้ดตัวอย่างดังต่อไปนี้ (และสร้างตัวอย่าง Codesandbox สำหรับการทดสอบ)
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));
}
ตอนนี้ฉันไม่รู้วิธีสร้างสคีมาการตรวจสอบความถูกต้องสำหรับสมบัติ myComplexType ฉันพบการอภิปรายบางส่วน
- เพิ่มคุณสมบัติ instanceof สำหรับวัตถุประเภท
- การสร้างประเภทที่กำหนดเอง
- พิมพ์คำสำคัญ
แต่ฉันไม่คิดว่าสิ่งเหล่านี้จะช่วยได้เพราะ
typeofจะคืนค่าแค่"object"instanceofจะใช้กับประเภทไม่ได้
แล้วฉันต้องสร้างคีย์เวิร์ดแบบกำหนดเอง (ตามที่อธิบายไว้ที่นี่) และเขียนตรรกะการตรวจสอบของตัวเอง (ตรวจสอบโครงสร้างของวัตถุ) หรือมีสิ่งที่ฉันสามารถใช้ได้แล้วบ้าง? ฉันควรตั้งค่า myComplexType อย่างไร?
คำตอบ #1
ฉันปรับโครงสร้างของคุณให้แบนเพื่อที่จะสามารถตอบด้วยโค้ดเพียงส่วนเดียวได้ นี่คือ TypeScript และสามารถรันบน NODE JS ได้สำเร็จโดยใช้ Ajv
กุญแจสำคัญคือต้องใช้ JSONSchemaType จาก Ajv เพื่อเชื่อมระหว่าง Ajv และ TypeScript หาก TYPE และคำนิยามของ AJV ไม่ตรงกัน TypeScript จะเตือน นี่เป็นเรื่องดี โดยเฉพาะอย่างยิ่งถ้าเครื่องมือแก้ไขของคุณมีตัวตรวจสอบ TypeScript แบบเรียลไทม์ในตัว (VS Code, VIM, ...)
สังเกตว่า schema MyComplexTypeSchema ถูกกำหนดโดยใช้ schemas ที่กำหนดไว้ก่อนหน้านี้ มันไม่จำเป็นต้องเป็น schema ขนาดใหญ่ที่ซ้อนกันทั้งหมด ให้มี schema สำหรับทุก TYPE และ INTERFACE และสร้างไลบรารีของคุณจากพื้นฐานเพื่อรองรับทั้ง TypeScript และ 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));
}
ฉันทิ้งโค้ด "const schema" ของคุณไว้ตามเดิม แต่โค้ดนั้นน่าจะเป็นอะไรทำนองด้านล่าง เพื่อให้สอดคล้องกับการจัดโครงสร้าง Type/Schema อื่น ๆ
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"
}
};
คำตอบ #2
ฉันไม่คิดว่าคุณสามารถสร้าง type แบบกำหนดเองด้วยไลบรารีนี้ได้ บางทีคุณอาจสับสนระหว่าง javascript types กับ typescript types ดังนั้นคุณน่าจะมีทางเลือก 2 ทาง: 1) แทนที่จะใช้ types คุณใช้ classes และใช้ instanceof เพื่อตรวจสอบตัวอย่างของ class ของคุณ เช่น
class MyClass {}
const instanceofDef = require("ajv-keywords/dist/definitions/instanceof")
instanceofDef.CONSTRUCTORS.MyClass = MyClass
ajv.validate({instanceof: "MyClass"}, new MyClass())
หรือ 2) คุณสร้างสคีมาแบบซ้อนเช่นนี้
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);
