ThomasAribart / json-schema-to-ts

Infer TS types from JSON schemas 📝
MIT License
1.4k stars 30 forks source link

Inconsistent type inference for `additionalProperties` when `properties` is also defined #199

Open alvis opened 2 months ago

alvis commented 2 months ago

Encountering a bug in json-schema-to-ts version 3.1.0 where the handling of additionalProperties does not work as expected when properties is also provided. Instead of assigning the designated type to additional properties, they are treated as unknown.

Example to reproduce:

import type { FromSchema } from 'json-schema-to-ts';

type Output = FromSchema<{
  type: 'object';
  additionalProperties: { type: 'string' };
  properties: {
    something: { type: 'string' };
  };
}>;

Expected behavior:

type Output = {
    [x: string]: string | undefined;
    something?: string | undefined;
}

Actual behavior:

type Output = {
    [x: string]: unknown;
    something?: string | undefined;
}

The expected behavior would be to treat all additional properties as string or string | undefined (which makes more sense when it comes to dealing with the unknown and type compliant to the non required additional properties), aligning with the specified additionalProperties type.

ThomasAribart commented 2 months ago

Hi @alvis ! This was already an issue before v3.1, so I would consider this an improvement rather than a bug.

As specified in the documentation, what is tricky is that additionalProperties and properties could be in conflict once transformed into types:

const validSchema = {
  type: "object",
  additionalProperties: { enum: ["foo", "bar"] },
  properties: {
    something: { type: "string" },
  },
} as const;

// { [x: string]: "foo" | "bar"; something: string; }
type InvalidType = FromSchema<typeof validSchema>;

// 'something' is an example of string so TS expects 'foo' or 'bar'
const conflict: InvalidType = { something: "baz" };

There is a case however for keeping the type of additionalProperties if it extends the union of all the properties. This is a bit tricky to do but can probably be done in https://github.com/ThomasAribart/ts-algebra. I'll have a look.