使用 AJV 进行混合类型打字稿验证

Mixed Type Typescript Validation with AJV

提问人:Tony 提问时间:6/12/2023 最后编辑:Tony 更新时间:6/13/2023 访问量:238

问:

enter image description here因此,我正在尝试使用 Ajv npm 包根据我们的打字稿类型验证 JSON 作为单一事实来源。我有这 99% 适用于多种类型/检查,但在特定混合类型中遇到了一个小问题。最初我试图减少示例,但它是无效的,所以我包含了整个类型(问题出在类型中都标记为)。我发现的唯一解决方法是将它放在架构的末尾,这否定了我试图采用的单一事实来源方法。任何见解将不胜感激!defaultValueModuleJsonSectionInputTHIS ONEas any

我正在使用 Ajv 的 8.12.0 版本。

打字稿类型

type InputType = 'input' | 'dropdown' | 'multiselect' | 'textarea' | 'richtext' | 'code-editor' | 'section-break' | 'toggle';

type Condition = 'equalTo' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqualTo' | 'lessThanOrEqualTo' | 'notEqualTo' | 'falsey';

interface InputValue {
  label: string;
  value: string;
}

interface ModuleJsonSections {
  collapsed?: boolean;
  inputs: ModuleJsonSectionInput[];
  label: string;
}

interface ConditionalRenderConfig {
  targetValueName: string;
  condition: Condition;
  compareValue: string | number | boolean;
}

interface ModuleJsonWebhookConfig {
  method: string;
  url: string;
  headers?: Record<string, string>;
  body?: Record<string, unknown> | string;
  isSearchOnly?: boolean;
}

interface ModuleJsonWebhookConfigValuesResponse extends ModuleJsonWebhookConfig {
  responsePath: string;
  labelPath: string;
  valuePath: string;
}

interface ModuleJsonSectionInput {
  label: string;
  name: string;
  type: InputType;
  subtype?: string;
  valuesType: 'static' | 'api';
  values?: InputValue[];
  allowFreeInput?: boolean;
  api?: ModuleJsonWebhookConfigValuesResponse;
  defaultValue?: string | boolean | number | string[]; // THIS ONE
  conditionalRenderConfig?: ConditionalRenderConfig;
}

Javascript 架构有效但引发 Typescript 错误

export const sectionsSchema: JSONSchemaType<ModuleJsonSections[]> = {
  type: "array",
  default: [],
  items: {
    type: "object",
    required: [
      "label",
      "inputs"
    ],
    properties: {
      label: {
        type: "string"
      },
      inputs: {
        type: "array",
        items: {
          type: "object",
          required: [
            "label",
            "name",
            "type",
            "valuesType",
          ],
          properties: {
            label: {
              type: "string"
            },
            name: {
              type: "string"
            },
            type: {
              enum: [ "input", "dropdown", "multiselect", "textarea", "richtext", "code-editor", "section-break", "toggle" ],
              type: "string"
            },
            subtype: {
              type: "string",
              nullable: true,
            },
            valuesType: {
              enum: [ "static", "api" ],
              type: "string"
            },
            values: {
              type: "array",
              nullable: true,
              default: [],
              items: {
                type: "object",
                required: [
                  "label",
                  "value"
                ],
                properties: {
                  label: {
                    type: "string",
                  },
                  value: {
                    type: "string",
                  }
                }
              }
            },
            allowFreeInput: {
              type: "boolean",
              nullable: true,
            },
            api: {
              type: "object",
              nullable: true,
              required: [
                "responsePath",
                "labelPath",
                "valuePath",
                "method",
                "url",
              ],
              properties: {
                responsePath: {
                  type: "string"
                },
                labelPath: {
                  type: "string"
                },
                valuePath: {
                  type: "string"
                },
                method: {
                  type: "string"
                },
                url: {
                  type: "string"
                },
                headers: {
                  type: "object",
                  required: [],
                  nullable: true
                },
                body: {
                  type: "object",
                  required: [],
                  nullable: true
                },
                isSearchOnly: {
                  type: "boolean",
                  nullable: true
                },
              },
            },
            defaultValue: { // THIS ONE
              oneOf: [
                {
                  type: "array",
                  nullable: true,
                  items: {
                    type: "string",
                  },
                },
                {
                type:[
                    "string",
                    "boolean",
                    "integer",
                  ],
                  nullable: true,
                },
              ],
            },
            conditionalRenderConfig: {
              type: "object",
              nullable: true,
              required: [
                "targetValueName",
                "condition",
                "compareValue"
              ],
              properties: {
                targetValueName: {
                  type: "string"
                },
                condition: {
                  type: "string",
                  enum: [ "equalTo", "greaterThan", "lessThan", "greaterThanOrEqualTo", "lessThanOrEqualTo", "notEqualTo", "falsey", ],
                },
                compareValue: {
                  type: [ "string", "integer", "boolean" ],
                }
              }
            }
          }
        }
      },
      collapsed: {
        type: "boolean",
        nullable: true,
      }
    }
  }
};

TypeScript 错误

Type '{ type: "array"; default: never[]; items: { type: "object"; required: ("label" | "inputs")[]; properties: { label: { type: "string"; }; inputs: { type: "array"; items: { type: "object"; required: ("type" | "label" | "name" | "valuesType")[]; properties: { ...; }; }; }; collapsed: { ...; }; }; }; }' is not assignable to type 'JSONSchemaType<ModuleJsonSections[]>'.
  Types of property 'items' are incompatible.
    Type '{ type: "object"; required: ("label" | "inputs")[]; properties: { label: { type: "string"; }; inputs: { type: "array"; items: { type: "object"; required: ("type" | "label" | "name" | "valuesType")[]; properties: { ...; }; }; }; collapsed: { ...; }; }; }' is not assignable to type 'UncheckedJSONSchemaType<ModuleJsonSections, false>'.
      The types of 'properties.inputs' are incompatible between these types.
        Type '{ type: "array"; items: { type: "object"; required: ("type" | "label" | "name" | "valuesType")[]; properties: { label: { type: "string"; }; name: { type: "string"; }; type: { enum: ("input" | "dropdown" | ... 5 more ... | "toggle")[]; type: "string"; }; ... 6 more ...; conditionalRenderConfig: { ...; }; }; }; }' is not assignable to type '{ $ref: string; } | (UncheckedJSONSchemaType<ModuleJsonSectionInput[], false> & { nullable?: false | undefined; const?: ModuleJsonSectionInput[] | undefined; enum?: readonly ModuleJsonSectionInput[][] | undefined; default?: ModuleJsonSectionInput[] | undefined; })'.
          Types of property 'items' are incompatible.
            Type '{ type: "object"; required: ("type" | "label" | "name" | "valuesType")[]; properties: { label: { type: "string"; }; name: { type: "string"; }; type: { enum: ("input" | "dropdown" | "multiselect" | "textarea" | "richtext" | "code-editor" | "section-break" | "toggle")[]; type: "string"; }; ... 6 more ...; conditionalR...' is not assignable to type 'UncheckedJSONSchemaType<ModuleJsonSectionInput, false>'.
              The types of 'properties.defaultValue' are incompatible between these types.
                Type '{ oneOf: ({ type: "array"; nullable: boolean; items: { type: "string"; }; } | { type: ("string" | "boolean" | "integer")[]; nullable: boolean; })[]; }' is not assignable to type '{ $ref: string; } | (UncheckedJSONSchemaType<string | number | boolean | string[] | undefined, false> & { nullable: true; const?: null | undefined; enum?: readonly (string | ... 4 more ... | undefined)[] | undefined; default?: string | ... 4 more ... | undefined; })'.
                  Type '{ oneOf: ({ type: "array"; nullable: boolean; items: { type: "string"; }; } | { type: ("string" | "boolean" | "integer")[]; nullable: boolean; })[]; }' is not assignable to type '{ type: "array"; items: UncheckedJSONSchemaType<string, false>; contains?: UncheckedPartialSchema<string> | undefined; minItems?: number | undefined; ... 4 more ...; additionalItems?: undefined; } & { ...; } & { ...; } & { ...; }'.
                    Type '{ oneOf: ({ type: "array"; nullable: boolean; items: { type: "string"; }; } | { type: ("string" | "boolean" | "integer")[]; nullable: boolean; })[]; }' is missing the following properties from type '{ type: "array"; items: UncheckedJSONSchemaType<string, false>; contains?: UncheckedPartialSchema<string> | undefined; minItems?: number | undefined; ... 4 more ...; additionalItems?: undefined; }': type, itemsts(2322)
JSON 打字稿 验证 AJV

评论

0赞 Evert 6/13/2023
您共享的架构不是有效的 javascript。
0赞 Tony 6/13/2023
我已经更新了它以包含整个类型,并将相关问题标记为THIS ONE
0赞 Evert 6/13/2023
我已经根据 JSON 架构架构验证了您的架构(没有拼写错误),并且没有收到任何错误。这向我表明您使用的类型可能是错误的。
0赞 Tony 6/13/2023
感谢您对此进行调查!当我检查我的代码时,打字稿错误仍然存在,这意味着类型与架构验证不一致(它们与我所做的所有其他类型对齐,并且不会在这些类型上产生错误)。我包含了原始问题的屏幕截图,以显示 + 更新了打字稿错误以显示整个错误消息,以防其他部分相关!
0赞 Evert 6/14/2023
如果删除,错误是否仍然发生?如果是,这表明 JSONSchemaType 中存在错误<ModuleJsonSections[]>

答: 暂无答案