typescriptajv

Ajv JSONSchemaType error using typescript


I'm having an issue writing a validator with Ajv and typescript.

Here is the definition of my schema

import Ajv, { JSONSchemaType } from "ajv";
const ajv = new Ajv();

interface Attribute {
  name: string;
  type: string;
  ref?: boolean;
  cardinality?: string;
  optional?: boolean;
}

interface Concept {
  type: string;
  root?: boolean;
  attributes?: Array<Attribute>;
  hidden?: boolean;
  terminal?: boolean;
  expression?: string;
}

const properties = {
  name: { type: "string" },
  type: { type: "string" },
  ref: { type: "boolean" },
  cardinality: { type: "string"}, 
  optional: { type: "boolean" },
};

const conceptSchema: JSONSchemaType<Concept> = {
  type: "object",
  properties: {
    type: { type: "string" },
    root: { type: "boolean"},
    attributes: {
      type: "array",
      uniqueItems: true,
      items: {
        type: "object",
        properties: properties,
        required: ["name", "type"],
      },
    },
    hidden: { type: "boolean" },
    terminal: { type: "boolean" },
    expression: { type: "string" },
  },
  required: ["type"],
  additionalProperties: false,
};

Any idea why I'm getting the following error using ajv and typescript?

"message": "Type '{ type: "object"; properties: { type: { type: "string"; }; root: { type: "boolean"; }; attributes: { type: "array"; uniqueItems: true; items: { type: "object"; properties: { name: { type: string; }; type: { type: string; }; ref: { ...; }; cardinality: { ...; }; optional: { ...; }; }; required: ("type" | "name")[]; }...' is not assignable to type 'UncheckedJSONSchemaType<Concept, false>'.\n The types of 'properties.attributes' are incompatible between these types.\n
Type '{ type: "array"; uniqueItems: true; items: { type: "object"; properties: { name: { type: string; }; type: { type: string; }; ref: { type: string; }; cardinality: { type: string; pattern: string; }; optional: { type: string; }; }; required: ("type" | "name")[]; }; }' is not assignable to type '{ $ref: string; } | (UncheckedJSONSchemaType<Attribute[] | undefined, false> & { nullable: true; const?: null | undefined; enum?: readonly (Attribute[] | null | undefined)[] | undefined; default?: Attribute[] | ... 1 more ... | undefined; })'.\n Types of property 'items' are incompatible.\n
Type '{ type: "object"; properties: { name: { type: string; }; type: { type: string; }; ref: { type: string; }; cardinality: { type: string; pattern: string; }; optional: { type: string; }; }; required: ("type" | "name")[]; }' is not assignable to type 'UncheckedJSONSchemaType<Attribute, false>'.\n The types of 'properties.name' are incompatible between these types.\n
Type '{ type: string; }' is not assignable to type '{ $ref: string; } | (UncheckedJSONSchemaType<string, false> & { const?: string | undefined; enum?: readonly string[] | undefined; default?: string | undefined; })'.\n Type '{ type: string; }' is not assignable to type '{ type: "string"; } & StringKeywords & { allOf?: readonly UncheckedPartialSchema[] | undefined; anyOf?: readonly UncheckedPartialSchema[] | undefined; ... 4 more ...; not?: UncheckedPartialSchema<...> | undefined; } & { ...; } & { ...; }'.\n Type '{ type: string; }' is not assignable to type '{ type: "string"; }'.\n Types of property 'type' are incompatible.\n Type 'string' is not assignable to type '"string"'.",


Solution

  • Since your interface says that some properties can be undefined, you have to add that into your schema as nullable: true -

    const conceptSchema: JSONSchemaType<Concept> = {
        type: "object",
        properties: {
            type: { type: "string" },
            root: { type: "boolean", nullable: true },
            attributes: {
                type: "array",
                uniqueItems: true,
                items: {
                    type: "object",
                    properties: {
                        name: { type: "string" },
                        type: { type: "string" },
                        ref: { type: "boolean", nullable: true },
                        cardinality: { type: "string", nullable: true },
                        optional: { type: "boolean", nullable: true },
                    },
                    required: ["name", "type"],
                },
                nullable: true,
            },
            hidden: { type: "boolean", nullable: true },
            terminal: { type: "boolean", nullable: true },
            expression: { type: "string", nullable: true },
        },
        required: ["type"],
        additionalProperties: false,
    };
    

    Also note that I have inlined the constant properties so it won't be inferred incorrectly.

    Playground