Skip to content
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

Export types/interfaces instead of inlining objects #2033

Closed
1 task done
TkDodo opened this issue Dec 3, 2024 · 1 comment
Closed
1 task done

Export types/interfaces instead of inlining objects #2033

TkDodo opened this issue Dec 3, 2024 · 1 comment
Labels
enhancement New feature or request openapi-ts Relevant to the openapi-typescript library

Comments

@TkDodo
Copy link

TkDodo commented Dec 3, 2024

Description

Right now, openapi-typescript returns inlined objects for components. For example, the github api has an operation for getting the readme:

'repos/get-readme': {
  responses: {
    /** @description Response */
      200: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['content-file']
        }
      }
    }
  }
}

(note: I’ve left out parameters for brevity)

Here, this operation references components['schemas']['content-file'], which is correct, but 'content-file' is an inline object on components['schemas']:

/**
 * Content File
 * @description Content File
 */
'content-file': {
    type: 'file'
    encoding: string
    size: number
    name: string
}

(note: I’ve only shown some properties for brevity)

** Why is this a problem **

We are working in a composite TypeScript monorepo with declarationEmit. That means each package in he monorepo will create a .d.ts file of its exported content for other packages to consume (instead of consuming the source code directly). This greatly speeds-up type performance in large monorepos.

The problem is that, because types are inlined here, whenever we use a function that returns something from our contracts (e.g. with openapi-fetch), the inferred type is also inlined.

This can create huge declaration files, and we’ve already hit the limit, where TypeScript errors out with:

TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.

Some of our endpoint responses are inlined multiple times in the emitted declaration, which is the main cause of reaching the maximum length.

Potential solution

TypeScript will correctly keep import references when packages have their own exported types or interfaces. I was hoping that the --root-types config option would achieve exactly that, but it does not. All it does is create an extra, internally unused type that is in itself, a reference:

export type SchemaContentFile = components['schemas']['content-file'];

Proposal

I think a good solution would be to change how --root-types works, in a way that it doesn’t just reference components['schemas'], but that it becomes the definition for it. In the above example, if we have:

export type SchemaContentFile = {
    type: 'file'
    encoding: string
    size: number
    name: string
};

and then use this internally for components['schemas']['content-file']:

/**
 * Content File
 * @description Content File
 */
'content-file': SchemaContentFile

I think it would work the same as before for external consumers of SchemaContentFile, but it would allow openapi-fetch to infer the response of a get request towards repos/get-readme to be inferred as a reference to SchemaContentFile rather than an inline type.

Note that operations doesn’t need to change at all - it can still just refer to components['schemas']['content-file'], because that would then be a reference to SchemaContentFile.

Note: It doesn’t matter if those are exported as interface or type - TypeScript will keep importing the root type in both ways.

Checklist

We are currently in the process of trying out to change --root-types in the proposed direction for us. If you agree on that improvement, we’ll gladly commit it upstream.

@TkDodo TkDodo added enhancement New feature or request openapi-ts Relevant to the openapi-typescript library labels Dec 3, 2024
@drwpow
Copy link
Contributor

drwpow commented Dec 10, 2024

Sorry; commented confused how it differed from --root-types but you did explain that.

This is something we won’t consider. If you read back through issues going 5+ years, you’d find that exporting top-level types are a minefield of drawbacks, and it limits what OpenAPI can generate. Going further in this direction is a path we‘ve already gone down long ago, and we won’t revisit because at a certain point it breaks because it’s too abstracted from your original schema, myriad things have to be renamed to prevent conflict (not to mention special characters removed). Many people agree with you that the ergonomics are better! No question! But for more advanced usecases this limits how your schema can be named & composed, and this project will always err on the side of “support everyone, even really weird schemas” rather than have opinions. There are lots of opinionated libraries that are good solutions for people.

@drwpow drwpow closed this as completed Dec 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request openapi-ts Relevant to the openapi-typescript library
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants