Skip to content

[BUG][typescript-fetch] instanceOf type guards produce "TS2590: union type too complex" for models with many sanitized (snake_case) required properties #23980

@jwhitcraft

Description

@jwhitcraft

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?

Description

As of 7.23.0, the typescript-fetch generator emits instanceOf type guards that fail to compile with error TS2590: Expression produces a union type that is too complex to represent when a model has many required properties whose name differs from baseName (e.g. snake_case JSON keys camelCased into the TS interface).

This is a regression introduced by #23497, which added a dual-key check guarded by hasSanitizedName in modelGeneric.mustache:

{{#hasSanitizedName}}
if ((!('{{name}}' in value) && !('{{baseName}}' in value)) || (value['{{name}}'] === undefined && value['{{baseName}}'] === undefined)) return false;
{{/hasSanitizedName}}

Because the function is a type predicate (value is {{classname}}) and each clause performs two indexed-access reads (value['camelCase'], value['snake_case']) against a parameter typed object, TypeScript computes a narrowed candidate union after every clause. For a model where most/all required fields are sanitized (any snake_case API), the union width grows past the compiler's internal limit and trips TS2590. Skinny models stay under the threshold; wide models (≈15+ sanitized required props) fail.

APIs that serialize snake_case are hit on nearly every model, since name !== baseName for essentially every field.

openapi-generator version

7.23.0 (regression introduced in this version; 7.22.0 and earlier generate compilable guards). Latest release at time of filing.

OpenAPI declaration / minimal spec

openapi: 3.0.3
info:
  title: repro
  version: 1.0.0
paths:
  /thing:
    get:
      operationId: getThing
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Thing'
components:
  schemas:
    Thing:
      type: object
      properties:
        aws_region:        { type: string }
        aws_account_name:  { type: string }
        aws_account_number:{ type: string }
        created_at:        { type: string }
        updated_at:        { type: string }
        # ...repeat enough snake_case required props to cross the union limit (~15+)
      required:
        - aws_region
        - aws_account_name
        - aws_account_number
        - created_at
        - updated_at

Generation command

openapi-generator-cli generate -i repro.yaml -g typescript-fetch -o ./out
cd out && tsc --noEmit

Generated output (abbreviated)

export function instanceOfThing(value: object): value is Thing {
    if ((!('awsRegion' in value) && !('aws_region' in value)) || (value['awsRegion'] === undefined && value['aws_region'] === undefined)) return false;
    // ...one per required field...
    return true;
}

tsc reports TS2590 on the value['...'] === undefined reads.

Steps to reproduce

  1. Generate the spec above with typescript-fetch on 7.23.0.
  2. Run tsc --noEmit against the output.
  3. Observe TS2590 on the instanceOf guard.

Suggested fix

A few directions (maintainers' call):

Related issues/PRs

Regression from #23497 (merged in 7.23.0).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions