Vyhledávání na webu

javascript - ajv - Zajistěte, aby struktura daného objektu odpovídala struktuře vlastního typu


Používám ajv s TypeScriptem a mám vlastní typ MyCustomType. Při vytváření validačního schématu chci zajistit, aby konkrétní vlastnost byla typu MyCustomType. Ajv by tedy měl ověřit jeho strukturu a rozhodnout, zda by to mohlo být parsováno na daný typ.

Začal jsem s následujícím ukázkovým kódem (a vytvořil jsem pro testovací účely příklad v Codesandboxu)

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

V současné době nevím, jak vytvořit validační schéma pro vlastnost myComplexType. Našel jsem několik diskusí

  • Přidejte vlastnost instanceof pro objekty typu
  • Vytváření vlastního typu
  • Zadejte klíčová slova

ale nemyslím si, že by to pomohlo, protože

  • typeof prostě vrací "object"
  • instanceof nebude fungovat pro typy

Takže musím vytvořit vlastní klíčové slovo (jak je popsáno zde) a napsat si vlastní validační logiku (prozkoumat strukturu objektu) nebo existují nějaké věci, které mohu už použít? Jak bych měl nakonfigurovat myComplexType?

Odpověď č. 1

Zploštil jsem vaši strukturu, abych mohl odpovědět jedním segmentem kódu. Toto je TypeScript a úspěšně běží na NODE JS s použitím Ajv.

Klíčem je použití JSONSchemaType z Ajv pro propojení mezi Ajv a TypeScriptem. Pokud se definice TYPE a AJV neshodují, TypeScript si stěžuje. To je skvělé, zejména pokud má váš editor zabudovaný real-time kontrolor TypeScriptu (VS Code, VIM, ...).

Všimněte si, že schéma MyComplexTypeSchema je definováno pomocí dříve definovaných schémat. Nemusí to být jedno obrovské vnořené schéma. Mějte schéma pro každý TYPE a INTERFACE a budujte svou knihovnu od základů tak, aby podporovala jak TypeScript, tak 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));
}

Nechal jsem váš kód "const schema"" beze změny, ale pravděpodobně by měl být něco podobného níže, aby to bylo konzistentní s ostatní strukturou 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"
  }
};

Odpověď č. 2

Nemyslím si, že s touto knihovnou můžete vytvořit vlastní typ, možná si pletete javascriptové typy s typescriptovými typy. Takže pravděpodobně máte 2 možnosti: 1) místo používání typů použijete třídy a pro ověření instance vaší třídy použijete instanceof, např.

class MyClass {}
const instanceofDef = require("ajv-keywords/dist/definitions/instanceof")
instanceofDef.CONSTRUCTORS.MyClass = MyClass
ajv.validate({instanceof: "MyClass"}, new MyClass())

nebo 2) vytvoříte vnořený schéma takto

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