question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to handle circular references caused by polymorphism

See original GitHub issue

Hello, I’m running into issues with circular references caused by a setup that contains polymorphism in the classes. I’m wondering how to handle these cases.

Take the below schema as an example. BlockDTO has type property indicating the type of BlockDTO. It can be a CsvBlockDTO or a FileBlockDTO.

Schema

schemas:
    BlockDTO:
      required:
      - title
      type: object
      properties:
        title:
          type: string
          nullable: false
      discriminator:
        propertyName: type
        mapping:
          csv: '#/components/schemas/CsvBlockDTO'
          file: '#/components/schemas/FileBlockDTO'
      oneOf:
      - $ref: '#/components/schemas/CsvBlockDTO'
      - $ref: '#/components/schemas/FileBlockDTO'
    CsvBlockDTO:
      allOf:
      - $ref: '#/components/schemas/BlockDTO'
      - required:
        - text
        - type
        type: object
        properties:
          type:
            type: string
            default: csv
            enum:
            - csv
          text:
            type: string
            nullable: false
    FileBlockDTO:
      allOf:
      - $ref: '#/components/schemas/BlockDTO'
      - required:
        - fileId
        - type
        type: object
        properties:
          type:
            type: string
            default: file
            enum:
            - file
          fileId:
            type: string
            nullable: false

This schema is generated as below, but clearly results in typescript errors due to BlockDTO referencing CsvBlockDTO while CsvBlockDTO is also referencing BlockDTO.

export type BlockDTO = (CsvBlockDTO | FileBlockDTO) & {
  title: string;
};

export type CsvBlockDTO = BlockDTO & {
  /** @default "csv" */
  type: "csv";
  text: string;
};

export type FileBlockDTO = BlockDTO & {
  /** @default "file" */
  type: "file";
  fileId: string;
};

I think the only way to solve this is to create some sort of abstract class, but it isn’t an option to configure on this generator. Nor did I find it to be an option on any of the other openapi->typescript generators I found so far.

export interface AbstractBlockDTO {
  type: "csv" | "file";
  title: string;
}

export type BlockDTO = CsvBlockDTO | FileBlockDTO;

export type CsvBlockDTO = AbstractBlockDTO & {
  type: "csv";
  text: string;
};

export type FileBlockDTO = AbstractBlockDTO & {
  type: "file";
  fileId: string;
};

Any thoughts / suggestions?

Issue Analytics

  • State:open
  • Created 10 months ago
  • Comments:21 (12 by maintainers)

github_iconTop GitHub Comments

1reaction
evtkcommented, Nov 15, 2022

Yes, well I’m then wondering if that swagger schema for Dog is valid. I think that I would expect the following:

when Pet has this mapping inside the descriminator

"discriminator": {
        "propertyName": "pet_type",
        "mapping": {
          "cachorro": "#/components/schemas/Dog",
          "cat": "#/components/schemas/Cat"
        }
      }

then Dog should always have that pet_type defined in its own schema as a required property.


 "Dog": {
    "allOf": [
        {
            "$ref": "#/components/schemas/Pet"
        },
        {
            "required": [
                "pet_type"
            ],
            "type": "object",
            "properties": {
                "pet_type": {
                    "type": "string",
                    "default": "cachorro",
                    "enum": [
                        "cachorro"
                    ]
                }
            }
        }
    ]
}

But I’m not an expert on the validity of those schemas 😃. I’ll contact my co-workers to bring this to their attention and to see what solutions/thoughts they can come up with.

0reactions
evtkcommented, Dec 13, 2022

Hey @js2me how are you doing? I see it has been a bit quiet around here and I’m wondering if we can do anything to assist to get things going. We are on the verge of starting big migration of DTO’s with the use of OpenAPI so we are definitely looking out for the above feature 👍🏻

Read more comments on GitHub >

github_iconTop Results From Across the Web

code quality - What's wrong with circular references?
Circular object references can crash naïve recursive algorithms (such as serializers, visitors and pretty-printers) with stack overflows. The ...
Read more >
How to fix nasty circular dependency issues once and for all in ...
Although there are many strategies and best practices on how to avoid circular dependencies. There is very little on how to fix them...
Read more >
How do I properly use polymorphism using component pattern ...
First things first, Why do I want to use a pointer instead of a reference on virtual void Update(Entity? entity); We were taught...
Read more >
Circular Referencing of Classes - YouTube
This video examines the problem created when two classes each instantiate each other. This causes a circular reference and you go into an ......
Read more >
How do we handle circular dependency between Python ...
Circular references can lead to memory leaks, which can be avoided in two ways i.e. manually erasing each reference and utilizing the weakref() ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found