bcherny/json-schema-to-typescript

`$ref` in `allOf` with depth of 2 is not correctly converted when multi-referenced

Closed this issue · 5 comments

I'm trying to reproduce an inheritance schema like this:

Thing
 |
Vehicle
 |     \ 
Car     Truck

Using the "allOf" keyword with a schema like this:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "test",
  "definitions": {
    "Thing": {
      "type": "object",
      "properties": {
        "name": { "type": "string" }
      },
      "required": ["name"]
    },
    "Vehicle": {
      "type": "object",
      "allOf": [{ "$ref": "#/definitions/Thing" }],
      "properties": {
        "year": { "type": "integer" }
      },
      "required": ["year"]
    },
    "Car": {
      "type": "object",
      "allOf": [{ "$ref": "#/definitions/Vehicle" }],
      "properties": {
        "numDoors": { "type": "integer" }
      },
      "required": ["numDoors"]
    },
    "Truck": {
      "type": "object",
      "allOf": [{ "$ref": "#/definitions/Vehicle" }],
      "properties": {
        "numAxles": { "type": "integer" }
      },
      "required": ["numAxles"]
    }
  },
  "oneOf": [{ "$ref": "#/definitions/Car" }, { "$ref": "#/definitions/Truck" }]
}

The output looks like this:

export type Test = Car | Truck;
export type Car = Car1 & {
  numDoors: number;
};
export type Car1 = Vehicle; // <--- This is expected
export type Vehicle = Vehicle1 & {
  year: number;
};
export type Vehicle1 = Thing;
export type Truck = Truck1 & {
  numAxles: number;
};
export type Truck1 = Vehicle1; // <--- This is unexpected, should be Vehicle
  name: string;
}

Here, Truck is directly inheriting Thing instead of Vehicle, so its TypeScript definition does not includes the year property.
If I exchange the properties of the root oneOf, then the the issue appears in the type definition of Car.

This does seem like a bug.

In your case, I think you actually want extends, which would fix it for you:

https://borischerny.com/json-schema-to-typescript-browser/#schema=%7B%0A%20%20%22$schema%22:%20%22http://json-schema.org/draft-07/schema#%22,%0A%20%20%22$id%22:%20%22test%22,%0A%20%20%22definitions%22:%20%7B%0A%20%20%20%20%22Thing%22:%20%7B%0A%20%20%20%20%20%20%22type%22:%20%22object%22,%0A%20%20%20%20%20%20%22properties%22:%20%7B%0A%20%20%20%20%20%20%20%20%22name%22:%20%7B%20%22type%22:%20%22string%22%20%7D%0A%20%20%20%20%20%20%7D,%0A%20%20%20%20%20%20%22required%22:%20%5B%22name%22%5D%0A%20%20%20%20%7D,%0A%20%20%20%20%22Vehicle%22:%20%7B%0A%20%20%20%20%20%20%22type%22:%20%22object%22,%0A%20%20%20%20%20%20%22extends%22:%20%7B%20%22$ref%22:%20%22#/definitions/Thing%22%20%7D,%0A%20%20%20%20%20%20%22properties%22:%20%7B%0A%20%20%20%20%20%20%20%20%22year%22:%20%7B%20%22type%22:%20%22integer%22%20%7D%0A%20%20%20%20%20%20%7D,%0A%20%20%20%20%20%20%22required%22:%20%5B%22year%22%5D%0A%20%20%20%20%7D,%0A%20%20%20%20%22Car%22:%20%7B%0A%20%20%20%20%20%20%22type%22:%20%22object%22,%0A%20%20%20%20%20%20%22extends%22:%20%7B%20%22$ref%22:%20%22#/definitions/Vehicle%22%20%7D,%0A%20%20%20%20%20%20%22properties%22:%20%7B%0A%20%20%20%20%20%20%20%20%22numDoors%22:%20%7B%20%22type%22:%20%22integer%22%20%7D%0A%20%20%20%20%20%20%7D,%0A%20%20%20%20%20%20%22required%22:%20%5B%22numDoors%22%5D%0A%20%20%20%20%7D,%0A%20%20%20%20%22Truck%22:%20%7B%0A%20%20%20%20%20%20%22type%22:%20%22object%22,%0A%20%20%20%20%20%20%22extends%22:%20%7B%20%22$ref%22:%20%22#/definitions/Vehicle%22%20%7D,%0A%20%20%20%20%20%20%22properties%22:%20%7B%0A%20%20%20%20%20%20%20%20%22numAxles%22:%20%7B%20%22type%22:%20%22integer%22%20%7D%0A%20%20%20%20%20%20%7D,%0A%20%20%20%20%20%20%22required%22:%20%5B%22numAxles%22%5D%0A%20%20%20%20%7D%0A%20%20%7D,%0A%20%20%22oneOf%22:%20%5B%7B%20%22$ref%22:%20%22#/definitions/Car%22%20%7D,%20%7B%20%22$ref%22:%20%22#/definitions/Truck%22%20%7D%5D%0A%7D

Thank you for your answer @bcherny !
extends will indeed generate the correct typescript classes, but then the validation will be incorrect, because the properties of the "parent" objets won't be required anymore and {"numDoors": 2} will become a valid input: https://jsonschema.dev/s/TdKME

@arnauddrain extends is an old keyword that has been removed in modern JSON Schema versions. The website you linked doesn't seem to implement it correctly. If your validator supports it, extends will work fine the way I suggested.

See the spec: https://github.com/json-schema/json-schema/wiki/Extends/014e3cd8692250baad70c361dd81f6119ad0f696

Merged #603

Fix included in v15.0.0.