texttechne / max-call-stack-size-exceeded

Demo for TS bug

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Demonstration of Max Call Stack Exceeded Error

This repo reproduces a specific TypeScript bug, which only occurs at a certain level of complexity.

The code to check is generated by odata2ts which generates an OData client out of a given metadata file (see folder odata). The generated code is to be found under build.

Now some generated code can be type checked (tsc) without error, while other code fails badly with: RangeError: Maximum call stack size exceeded. Unfortunately, the stack trace differs from case to case. You have the following commands:

  • npm run test-trippin (works)
  • npm run test-trippinBi (works)
  • npm run test-casePacer (fails)

Larger services with large metadata files, which lead to more generated code, will eventually result in this error.

As the metadata of the OData service represents an entity relationship model, there's also some form of recursion involved when entities have a bidirectional relationship to each other. However, this seems fine when the OData service in question is not that large (see the trippinBi example).

The Offending Code

So the error only occurs, when

  • services are generated (files with suffix "Service")
  • the OData service itself is fairly big (> 250kb metadata file)
  • and probably when recursion enters through the data model

Drilling down into the code, the error definitely won't occur when leaving out certain stuff:

  1. relationships to other entities
  2. relationships to entity collections

The funny thing in the CasePacer example (250 KB): I can leave out either 1 or 2 and it works.

For the dynamics example (16-20MB) both have to be left out.

Generated code for 1:

export class AppointmentService<
  ClientType extends ODataHttpClient
> extends EntityTypeServiceV4<
  ClientType,
  Appointment,
  EditableAppointment,
  QAppointment
> {
  private _AppointmentType?: AppointmentTypeService<ClientType>;
  //...
  
  public AppointmentType(): AppointmentTypeService<ClientType> {
    if (!this._AppointmentType) {
      const { client, path } = this.__base;
      this._AppointmentType = new AppointmentTypeService(
        client,
        path,
        "AppointmentType"
      );
    }

    return this._AppointmentType;
  }
  // ...
}

Generated code for 2:

export class AppointmentService<
  ClientType extends ODataHttpClient
> extends EntityTypeServiceV4<
  ClientType,
  Appointment,
  EditableAppointment,
  QAppointment
> {
  // ...
  
  public AppointmentParties(): AppointmentPartyCollectionService<ClientType>;
  public AppointmentParties(
    id: AppointmentPartyId
  ): AppointmentPartyService<ClientType>;
  public AppointmentParties(id?: AppointmentPartyId | undefined) {
    const fieldName = "AppointmentParties";
    const { client, path } = this.__base;
    return typeof id === "undefined" || id === null
      ? new AppointmentPartyCollectionService(client, path, fieldName)
      : new AppointmentPartyService(
        client,
        path,
        new QAppointmentPartyId(fieldName).buildUrl(id)
      );
  }
}

I also had one example where it was enough to leave out the overloads at this place and the error didn't occur.

About

Demo for TS bug


Languages

Language:TypeScript 100.0%