ThomasAribart / json-schema-to-ts

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

Type issue in oneOf schemas #116

Closed sceccotti89 closed 1 year ago

sceccotti89 commented 1 year ago

In a oneOf schema, the most nested property is not properly typed by TS.

Example:

const catSchema = {
  type: 'object',
  properties: {
    cat_property: {
      type: 'object',
      oneOf: [
        {
          properties: {
            name: { type: 'string' },
          },
          required: ['name'],
        },
        {
          properties: {
            color: { type: 'string', enum: ['black', 'brown', 'white'] },
          },
        },
      ],
    },
  },
} as const;

type Cat = FromSchema<typeof catSchema>;
const cat: Cat = { cat_property: { name: 'My name' } };
cat.cat_property.name; // This is considered unknown instead of string.

If I hover over the Cat type, everything looks fine:

type Cat = {
    [x: string]: unknown;
    cat_property: {
        [x: string]: unknown;
        name: string;
    } | {
        [x: string]: unknown;
        color?: "black" | "brown" | "white" | undefined;
    };
}

But when I try to access the name attribute, TS says it's unknown.

Is it a bug of the library or it's due to Typescript?

giraffesyo commented 1 year ago

Looks like you need to set additionalProperties: false,, along with the oneOf.

sceccotti89 commented 1 year ago

@giraffesyo

Thanks for the response but it's still not working. This is the updated schema:

const catSchema = {
  type: 'object',
  additionalProperties: false,
  oneOf: [
    {
      additionalProperties: false,
      properties: {
        name: { type: 'string' },
      },
      required: ['name'],
    },
    {
      additionalProperties: false,
      properties: {
        color: { type: 'string', enum: ['black', 'brown', 'white'] },
      },
    },
  ],
}

I tried all the possible combinations (with and without additionalProperties) but none of them worked. Were you able to test it yourself and make it works?

I forgot to share details about my environment.

ThomasAribart commented 1 year ago

Hi @sceccotti89 !

Several elements here:

You can try not to assign the Cat type to your cat const but simply make sure it satisfies it:

// Will type-check cat against Cat type, but keep the "naturally inferred" cat type
const cat = { cat_property: { name: 'My name' } } satisfies Cat

Does that answer your issue ?