Skip to content

"AdditionalProperties" reflects onto existing properties and causes TypeScript compile errors #1657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 of 2 tasks
grepfruit19 opened this issue May 9, 2024 · 1 comment
Labels
bug Something isn't working openapi-ts Relevant to the openapi-typescript library

Comments

@grepfruit19
Copy link

grepfruit19 commented May 9, 2024

Description

When provided a schema that has additional properties, the generated TypeScript generates a "catch-all" key-value pair that causes compile errors.

Name Version
openapi-typescript 6.7.5
Node.js 20.11.0
OS + version macOS 14

Reproduction

Here is the JSON I'm converting

{
  "openapi": "3.0.1",
  "info": { "title": "TESTFILE", "version": "1.0.0" },
  "security": [{ "Authorization": [] }],
  "components": {
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "detailMessageArguments": {
            "type": "array",
            "items": { "type": "object" }
          },
          "detailMessageCode": { "type": "string" },
          "titleMessageCode": { "type": "string" },
          "headers": {
            "type": "object",
            "properties": {
              "accessControlAllowHeaders": {
                "type": "array",
                "items": { "type": "string" }
              },
              "accessControlAllowOrigin": { "type": "string" },
              "accessControlExposeHeaders": {
                "type": "array",
                "items": { "type": "string" }
              },
              "accessControlAllowCredentials": { "type": "boolean" },
              "accessControlMaxAge": { "type": "integer", "format": "int64" },
              "accessControlRequestHeaders": {
                "type": "array",
                "items": { "type": "string" }
              },
              "basicAuth": { "type": "string", "writeOnly": true },
              "ifUnmodifiedSince": { "type": "integer", "format": "int64" },
              "acceptCharset": {
                "type": "array",
                "items": { "type": "string" }
              },
              "all": {
                "type": "object",
                "additionalProperties": { "type": "string" },
                "writeOnly": true
              },
              "lastModified": { "type": "integer", "format": "int64" },
              "date": { "type": "integer", "format": "int64" },
              "contentLength": { "type": "integer", "format": "int64" },
              "ifModifiedSince": { "type": "integer", "format": "int64" }
            },
            "additionalProperties": {
              "type": "array",
              "items": { "type": "string" }
            }
          }
        }
      }
    }
  }
}

And here is the outputted TypeScript code:

export interface components {
  schemas: {
    ErrorResponse: {
      detailMessageArguments?: Record<string, never>[];
      detailMessageCode?: string;
      titleMessageCode?: string;
      headers?: {
        accessControlAllowHeaders?: string[];
        accessControlAllowOrigin?: string;
        accessControlExposeHeaders?: string[];
        accessControlAllowCredentials?: boolean;
        /** Format: int64 */
        accessControlMaxAge?: number;
        accessControlRequestHeaders?: string[];
        basicAuth?: string;
        /** Format: int64 */
        ifUnmodifiedSince?: number;
        acceptCharset?: string[];
        all?: {
          [key: string]: string;
        };
        /** Format: int64 */
        lastModified?: number;
        /** Format: int64 */
        date?: number;
        /** Format: int64 */
        contentLength?: number;
        /** Format: int64 */
        ifModifiedSince?: number;
        [key: string]: string[] | undefined; // <------------ This is the line that's causing issues
      };
    };
  };
  responses: never;
  parameters: never;
  requestBodies: never;
  headers: never;
  pathItems: never;
}

If you attempt to parse this in TypeScript, it will give you errors such as

Property 'ifModifiedSince' of type 'number | undefined' is not assignable to 'string' index type 'string[] | undefined'.ts(2411)

This is occurring because additionalProperties is telling the object that all of its values should be string[], as opposed to just unlisted values.

Expected result

I don't know what the expected result should be honestly, but this doesn't compile so certainly not this. I'm not sure if this is a limitation of TypeScript as a language, if it is, maybe just generate something like [key: string]: unknown?

Checklist

@grepfruit19 grepfruit19 added bug Something isn't working openapi-ts Relevant to the openapi-typescript library labels May 9, 2024
@drwpow
Copy link
Contributor

drwpow commented May 9, 2024

This is a known issue and 7.x will NOT have | undefined for this very reason. It originally meant to improve type safety and discourage use of additionalProperties, but has caused issues in 6.x. Changing this would be a breaking change for many people, so it’s saved for the next major release.

This is already largely captured in #1055 (and other discussions); feel free to add to additional issues with more info if you’d like.

@drwpow drwpow closed this as completed May 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working openapi-ts Relevant to the openapi-typescript library
Projects
None yet
Development

No branches or pull requests

2 participants