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

Recursive generic types infinitely recurse when generating routes #1547

Open
2 of 4 tasks
jancastor opened this issue Jan 24, 2024 · 1 comment
Open
2 of 4 tasks

Recursive generic types infinitely recurse when generating routes #1547

jancastor opened this issue Jan 24, 2024 · 1 comment

Comments

@jancastor
Copy link

Sorting

  • I'm submitting a ...

    • bug report
    • feature request
    • support request
  • I confirm that I

    • used the search to make sure that a similar issue hasn't already been submit

Steps to repro

  1. Create a controller class file with the following contents
import { Get } from "api-external/src/rest/decorators/methods";

import { Controller, Route } from "tsoa";

/**
 * If T is an object, it returns T with an additional ID property
 * Otherwise we just return T
 */
type WithIdForObjectTypes<T> = T extends Record<string, any>
  ? AddIdToSubObjects<T> & { id: number }
  : T;

/**
 * This remaps a key-value mapping such that any object value has an additional ID property.
 * This is mutually recursive with WithIdForObjectTypes so that deeply nested objects also get the additional ID property.
 * e.g. { topLevelField: { subField: string } } -> { topLevelField: { subField: string, id: number } }
 *
 */
type AddIdToSubObjects<T extends Record<string, any>> = {
  [K in keyof T]: WithIdForObjectTypes<T[K]>;
};

/** Example case */
type MyObject = AddIdToSubObjects<{ topLevelField: { subField: string } }>;

@Route("example")
export class ExampleController extends Controller {
  @Get("example-endpoint")
  public async get() {
    const myObject: MyObject = {
      topLevelField: {
        subField: "foo",
        id: 1,
      },
    };

    return {
      myObject,
    };
  }
}
  1. Run tsoa routes

Expected Behavior

I would expect this type to correctly resolve when running the tsoa cli (tsoa spec-and-routes).

Specifically, the endpoint GET /example/example-endpoint should have a response type of

{ 
  topLevelField: {
    subField: string,
    id: number
  }
}

Current Behavior

The tsoa cli is erroring out with the following error.

/** MANY MORE SIMILAR LOG LINES OMITTED FOR BREVITY **/
There was a problem resolving type of 'WithIdForObjectTypes<{"topLevelField": {"subField": string}}[string][string][string][string]>'.
There was a problem resolving type of 'WithIdForObjectTypes<{"topLevelField": {"subField": string}}[string][string][string]>'.
There was a problem resolving type of 'WithIdForObjectTypes<{"topLevelField": {"subField": string}}[string][string]>'.
There was a problem resolving type of 'WithIdForObjectTypes<{"topLevelField": {"subField": string}}[string]>'.
There was a problem resolving type of 'AddIdToSubObjects<{"topLevelField": {"subField": string}}>'.
Generate routes error.
 RangeError: Maximum call stack size exceeded
    at writeOrBuffer (node:internal/streams/writable:553:21)
    at _write (node:internal/streams/writable:490:10)
    at Writable.write (node:internal/streams/writable:494:10)
    at console.value (node:internal/console/constructor:304:16)
    at console.warn (node:internal/console/constructor:384:26)
    at calcReferenceType (/workspaces/obsidian/node_modules/@tsoa/cli/dist/metadataGeneration/typeResolver.js:929:25)
    at TypeResolver.getReferenceType (/workspaces/obsidian/node_modules/@tsoa/cli/dist/metadataGeneration/typeResolver.js:933:24)
    at TypeResolver.resolve (/workspaces/obsidian/node_modules/@tsoa/cli/dist/metadataGeneration/typeResolver.js:513:36)
    at TypeResolver.resolve (/workspaces/obsidian/node_modules/@tsoa/cli/dist/metadataGeneration/typeResolver.js:303:106)
    at TypeResolver.getTypeAliasReference (/workspaces/obsidian/node_modules/@tsoa/cli/dist/metadataGeneration/typeResolver.js:1019:126)

It seems like it's infinitely recursing when trying to resolve the given type.

Context (Environment)

Version of the library: 6.0.1 (also tried with 6.0.0)
Version of NodeJS: 20.10.0

  • Confirm you were using yarn not npm: [x]
Copy link

Hello there jancastor 👋

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants