ค้นหาเว็บไซต์

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);