ajv-validator / ajv

The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
https://ajv.js.org
MIT License
13.85k stars 878 forks source link

Schema properties assigned `any` type if imported in another file #2076

Open knee-cola opened 2 years ago

knee-cola commented 2 years ago

When a schema gets imported to another file it's properties all get assigned any type.

This is based on the info displayed by the VS Code's Intellisense.

Here's an example I've tested inside a cloned Ajv repo v8.11.0:

The following snippet contains the schema definition for a Car object:

/* CarSchema.ts */

import { JSONSchemaType } from "../lib/ajv";

export type Car =
{
    carBrand: string,
    carModel: string,
};

export const CarSchema: JSONSchemaType<Car> = {
     type: "object",
     properties: {
         carBrand: { type: "string", minLength:1, maxLength:20 },
         carModel: { type: "string", minLength:1, maxLength:56 },
     },
     required: [ "carBrand", "carModel" ],
};

// (property) properties?: UncheckedPropertiesSchema<Car> | undefined
type PropertiesType = typeof CarSchem.properties;

As you can see the list line of the previous snippet, the type of the properties equals UncheckedPropertiesSchema<Car> | undefined, as expected.

This changes if you try importing and using this schema in another file:

/* BmwCarSchema.ts */

import { CarSchem } from "./CarSchema";

// (property) properties?: any
type PropertiesType = typeof CarSchem.properties;

As you can see properties has been assigned any type.

I've managed to create a workaround which does help with this issue but does not fix it.

The following file shows the workaround (only the last line has been added - the rest is unchanged):

/* CarSchema.ts */

import { JSONSchemaType } from "../lib/ajv";

export type Car =
{
    carBrand: string,
    carModel: string,
};

export const CarSchema: JSONSchemaType<Car> = {
     type: "object",
     properties: {
         carBrand: { type: "string", minLength:1, maxLength:20 },
         carModel: { type: "string", minLength:1, maxLength:56 },
     },
     required: [ "carBrand", "carModel" ],
};

// (property) properties?: UncheckedPropertiesSchema<Car> | undefined
type PropertiesType = typeof CarSchem.properties;

// setting type of the exported value
export const CarSchemaFixed: typeof CarSchema = CarSchema;

Now in our BmwCarSchema file use the newly exported value:

/* BmwCarSchema.ts */

import { CarSchem, CarSchemaFixed } from "./CarSchema";

// (property) properties?: any
type PropertiesType = typeof CarSchem.properties;

// (property) properties?: UncheckedPropertiesSchema<Car> | undefined
type PropertiesTypeFixed = typeof CarSchemaFixed.properties;

As you can see now the properties property is correctly typed (the last line).

I'm not sure this is the AJV issue or a TypeScript thing ...

epoberezkin commented 2 years ago

This is based on the info displayed by the VS Code's Intellisense.

Sorry, I would not trust what VS Code Intellisense shows... Please check the output of typescript compiler. If VSCode shows errors where typescript doesn't it should be reported to VSCode, not elsewhere ;)

I'm not sure this is the AJV issue or a TypeScript thing ...

The way you describe, it's VSCode thing :) please share tsc error output, if any.

Phault commented 2 years ago

I thought I was going crazy. Thank you so much for posting the workaround 🙏

Unfortunately the bug also occurs for function parameters:

function myFunction(schema: JSONSchemaType<{ someProperty: boolean }>) {
  // schema.properties?: any
  schema.properties.abc = ''; // no complaints from tsc
}

Since tsc doesn't complain, I don't think vscode is to blame. It happens with both typescript@4.7.2 and 4.8.3.

EDIT: I do not recommend using the mentioned workaround, it appears to make the ts-server very crashy.