ウェブサイト検索

javascript - ajv - 指定されたオブジェクトの構造がカスタム型の構造と一致することを確認する


私は TypeScript と一緒に ajv を使用しており、カスタム型 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

1つのコードセグメントで回答できるように、あなたの構造を平坦化しました。これはTypeScriptであり、Ajvを使用してNODE JS上で正常に動作します。

重要なのは、AjvのJSONSchemaTypeを使用して、AjvとTypeScriptの間を橋渡しすることです。TYPEとAJVの定義が一致しない場合、TypeScriptはエラーを出します。これは特に、エディタにTypeScriptのリアルタイムチェッカーが組み込まれている場合(VS Code、VIMなど)に便利です。

スキーマ MyComplexTypeSchema が以前に定義されたスキーマを使って定義されていることに注意してください。必ずしも一つの巨大なネストされたスキーマである必要はありません。すべての 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の型とtypescriptの型を混同しているのかもしれません。なので、おそらく2つの選択肢があります:1) 型を使う代わりにクラスを使用し、instanceofでクラスのインスタンスを検証する、例えば:

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