Search code examples
typescriptjsonschemadiscriminator

Unexpected result for TypeScript Interfaces using Quicktype 'allOf'


We are changing from JSON Type Definition to JSON Schema and are now using Quicktype to convert the JSON Schemas to TypeScript Types. For the most part, Quicktype is doing its job very well, but aparently it can't convert Discriminators, or the JSON Schema equivalent allOf with if-then. It seems to just ignore the allOf... Did we do anything wrong?

The bash command we use to convert JSON Schema to TypeScript:

quicktype -o ./src/typings.ts --just-types --acronym-style camel --src-lang schema

Expected Result

export type CreateCustomerCommandPayloadV1 = CreateCustomerCommandPayloadV1Person | CreateCustomerCommandPayloadV1Company;

export interface CreateCustomerCommandPayloadV1Person {
    type: CreateCustomerCommandPayloadV1Type.Person;
    customerKey: string
    firstName: string
    lastName: string
}
export interface CreateCustomerCommandPayloadV1Company {
  type: CreateCustomerCommandPayloadV1Type.Company;
  customerKey: string
  companyName: string
}

export enum CreateCustomerCommandPayloadV1Type {
    Company = "COMPANY",
    Person = "PERSON",
}

Actual Result

export interface CreateCustomerCommandPayloadV1 {
    type: CreateCustomerCommandPayloadV1Type;
}

export enum CreateCustomerCommandPayloadV1Type {
    Company = "COMPANY",
    Person = "PERSON",
}

Our JSON Schema File:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "metadata": {
    "description": "Command: Creates a new customer (Types: COMPANY | PERSON)",
    "subject": "commands.crm.customers.createCustomer",
    "authenticationRequired": true
  },
  "type": "object",
  "additionalProperties": false,
  "$id": "CreateCustomerCommandPayloadV1",
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "COMPANY",
        "PERSON"
      ]
    },
    "customerKey": {
      "type": "string"
    }
  },
  "required": [
    "type"
  ],
  "$comment": "discriminator",
  "allOf": [
    {
      "if": {
        "properties": {
          "type": {
            "const": "COMPANY"
          }
        }
      },
      "then": {
        "properties": {
          "companyName": {
            "type": "string"
          }
        },
        "required": [
          "companyName"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "type": {
            "const": "PERSON"
          }
        }
      },
      "then": {
        "properties": {
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
        },
        "type": "object",
        "additionalProperties": false,
        "title": "CreateCustomerCommandPayloadV1Person",
        "required": [
          "firstName",
          "lastName"
        ]
      }
    }
  ]
}

Solution

  • If you want to change your schema to use oneOf rather than if, then...these are the results. Still leaves a lot to be desired.

    {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "metadata": {
            "description": "Command: Creates a new customer (Types: COMPANY | PERSON)",
            "subject": "commands.crm.customers.createCustomer",
            "authenticationRequired": true
        },
        "type": "object",
        "$id": "CreateCustomerCommandPayloadV1",
        "oneOf": [
            {
                "type": "object",
                "properties": {
                    "type": {
                        "type": "string",
                        "enum": [
                            "PERSON"
                        ]
                    },
                    "customerKey": {
                        "type": "string"
                    },
                    "firstName": {
                        "type": "string"
                    },
                    "lastName": {
                        "type": "string"
                    }
                },
                "additionalProperties": false,
                "required": [
                    "type",
                    "firstName",
                    "lastName"
                ]
            },
            {
                "type": "object",
                "properties": {
                    "type": {
                        "type": "string",
                        "enum": [
                            "COMPANY"
                        ]
                    },
                    "companyName": {
                        "type": "string"
                    },
                    "customerKey": {
                        "type": "string"
                    }
                },
                "additionalProperties": false,
                "required": [
                    "type",
                    "companyName"
                ]
            }
        ]
    }
    
    export interface Typings {
        customerKey?: string;
        firstName?:   string;
        lastName?:    string;
        type:         Type;
        companyName?: string;
    }
    
    export enum Type {
        Company = "COMPANY",
        Person = "PERSON",
    }
    
    
    quicktype --out typings.ts --just-types  --acronym-style camel --src-lang schema --src schema.json --lang ts