lukeautry / tsoa

Build OpenAPI-compliant REST APIs using TypeScript and Node
MIT License
3.45k stars 497 forks source link

Type collision when extending a parent type or when the same variables are used. #1429

Open TrevorDMartin opened 1 year ago

TrevorDMartin commented 1 year ago

Sorting

Expected Behavior

All types are generated separate from each other. No field collision

Current Behavior

Types are colliding and adding fields to other types or overriding types with the same variable name

Types written like:

type Threshold = "HIGH" | "MEDIUM" | "LOW";
type Base = Record<
    string,
    {
        count: number;
        threshold?: Threshold;
    }
>;

interface PoInfo extends Base {
    type1: {
        count: number;
        threshold?: Threshold;
    };
}
interface Yard extends Base {
    cubes: {
        count: number;
        threshold?: Threshold;
    };
}

interface ParentType {
    yard: Yard;
    poInfo: PoInfo;
}

results in a schema like:

"schemas": {
    "Threshold": {
        "type": "string",
    "enum": [
        "HIGH",
        "MEDIUM",
        "LOW"
    ]
    },
    "Yard": {
        "properties": {
        "cubes": {
            "properties": {
            "threshold": {
                "$ref": "#/components/schemas/Threshold"
            },
                "count": {
                    "type": "number",
                "format": "double"
            }
        },
        "required": [
            "count"
        ],
        "type": "object"
    },

    "PoInfo": {
        "properties": {
        "cubes": {
            "properties": {
            "threshold": {
                "$ref": "#/components/schemas/Threshold"
            },
                "count": {
                    "type": "number",
                "format": "double"
            }
        },
        "required": [
            "count"
        ],
        "type": "object"
        },
        "type1": {
            "properties": {
            "threshold": {
                "$ref": "#/components/schemas/Threshold"
            },
                "count": {
                    "type": "number",
                "format": "double"
            }
        },
        "required": [
            "count"
        ],
        "type": "object"
        },       
        }
}

ALSO

If two different endpoints define the same variable for a type only one type is represented in the schema.

Steps to Reproduce

I made a repository with similar settings as the one I'm working in to reproduce the bug.

  1. git clone https://github.com/TrevorDMartin/tsoa-type-vs-interface/tree/main
  2. Follow steps in README.md to start application
  3. Go to http://localhost:8080/doc or see generated/swagger.json
  4. Types returned from /complex-schema have additional fields attached
  5. Types returned from '/collision-schema' have been overwritten by '/complex-schema`

Controller is located at src/schema-test/SchemaTestController.ts

There are three seperate files, one for each endpoint, where the data and types are defined.

Context (Environment)

Version of NodeJS: 18.15.0 Version of Typescript: ^5.0.4

Current Workaround

Changing the parent type in src/schema-test/ComplexTypes.ts from an interface to a type results in no more extra fields:

interface ComplexSchemaType {
    yard: Yard;
    poAge: PoAge;
    poInfo: PoInfo;
}

// TO 

type ComplexSchemaType = {
    yard: Yard;
    poAge: PoAge;
    poInfo: PoInfo;
}
github-actions[bot] commented 1 year ago

Hello there TrevorDMartin 👋

Thank you for opening your very first issue in this project.

We will try to get back to you as soon as we can.👀

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days

RayJSeth-kr commented 1 year ago

If two different endpoints define the same variable for a type only one type is represented in the schema.

~~I know this was the secondary issue described here, but this is the issue giving me the runaround today. Hypothetical example that follows the structure of what I'm trying to accomplish:~~

Upon further reading of the initial issue, the whole description is actually what I'm experiencing so I'll truncate my initial restating of the problem.

My thoughts on some potential solutions:

Ultimately I think if I had been presented with a warning or error that the collision occurred, just that would have been acceptable. Changing the variable name for the type was trivial, but it was trying to find out where the erasure of the second type was coming from that took time. Any chance of a config option to opt into a 'strict no collisions' mode?