ajv: JSONSchemaType doesn't infer nullable types

What version of Ajv are you using? Does the issue happen if you use the latest version? 8.11.2 This issue is not present in 8.11.0

Your typescript code

import { JSONSchemaType } from "ajv";

const schema: JSONSchemaType<{ foo: string | null }> = {
	type: "object",
	properties: {
		foo: { type: "string", nullable: true },
	},
	required: ["foo"],
};

void schema;

Typescript compiler error messages

Type '{ type: "object"; properties: { foo: { type: "string"; nullable: true; }; }; required: "foo"[]; }' is not assignable to type 'UncheckedJSONSchemaType<{ foo: string | null; }, false>'.
  The types of 'properties.foo' are incompatible between these types.
    Type '{ type: "string"; nullable: true; }' is not assignable to type '{ $ref: string; } | ({ anyOf: readonly UncheckedJSONSchemaType<string | null, false>[]; } & { [keyword: string]: any; $id?: string | undefined; $ref?: string | undefined; $defs?: Record<...> | undefined; definitions?: Record<...> | undefined; } & { ...; }) | ({ ...; } & ... 1 more ... & { ...; }) | ({ ...; } & ... 2...'.
      Types of property 'nullable' are incompatible.
        Type 'true' is not assignable to type 'false'.ts(2322)

Describe the change that should be made to address the issue? There should be no TypeScript errors. As mentioned, this does not give any compiler errors in version 8.11.0

Are you going to resolve the issue? I’m not familiar with the internals of ajv

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 26
  • Comments: 15

Most upvoted comments

Seem’s that this is a bug introduced with the commit https://github.com/ajv-validator/ajv/commit/00b3939ba545e87f585b5ee5e93d26f025454fc6

IMO it’s against the JSON Schema specification.

JSON Schema imposes no restrictions on type: JSON Schema can describe any JSON value, including, for example, null http://json-schema.org/draft/2020-12/json-schema-core.html

And from the link @juliensnz posted It’s important to remember that in JSON, null isn’t equivalent to something being absent.

With this change, it became impossible to define a required field that has a null value.

{
  type: "object",
  properties: {
    foo: { type: "null" },
  },
  required: ["foo"],
}

While it should be perfectly fine.

By the way, from this commit message “nullable was enforced for optional parameters” - I think this is also wrong. I might want to have an optional, string field that can’t be null if it’s present. In this case only a string should be accepted or undefined value. null shouldn’t be accepted. Nullable and non-required are different things similarly as null and “absent” are different concepts in JSON.

As a workaround I found that this works:

 type MyType = {
   field: string | null;
 };
 
 const schema: JSONSchemaType<MyType> = {
   $schema: 'http://json-schema.org/draft-07/schema#',
   type: 'object',
   required: [
     'field',
   ],
   properties: {
     'field': {
-      type: 'string',
-      pattern: '...',
-      nullable: true,
+      oneOf: [
+        {
+          type: 'string',
+          pattern: '...',
+        },
+        {
+          type: 'null',
+          nullable: true,
+        },
+      ],
     },
   },
 };

(though I’m not happy about it)

another solution (not prettier) could be to do:

type: ['string', 'null'] as unknown as 'string',

I will top this all with this workaround:

  { type: 'string', nullable: true as false }

Just see it as a Zen Koan

Hello everyone, who is encountered this issue! What helped me here to get rid of this issue at least temporary:

  1. Use normal scheme links, no need for ? or something else, like it was done in the initial issue message
import { JSONSchemaType } from "ajv";

const schema: JSONSchemaType<{ foo: string | null }> = {
	type: "object",
	properties: {
		foo: { type: "string", nullable: true },
	},
	required: ["foo"],
};

void schema;
  1. Use ajv version 8.11.0
  2. Use typescript version 5.0.4: for example typescript 5.2.2 proceeded reproduce this issue even with previously mentioned item.

Overall, looks like issue connected not only with ajv, but with typescript dependency changes too, unfortunately, there is not enough time for taking deeper look into possible reasons. Hope, that it will help someone to resolve it locally in your projects and maybe found the root cause and fix it in the next update.

This probably doesn’t help but it appears if you make the field optional (e.g. add ?) then the schema works.

e.g.

import { JSONSchemaType } from "ajv";

const schema: JSONSchemaType<{ foo?: string | null }> = {
	type: "object",
	properties: {
		foo: { type: "string", nullable: true },
	},
	required: ["foo"],
};

void schema;

In my case ajv was coercing null values to 0 since the nullable field was an integer – by adding ? I was able to add nullable: true and ajv also stopped coercing.