Skip to content

forwardRef run-time error with ES2015 output under bazel #29107

Closed

Description

@jelbourn

As of Angular version 8, the bazel setup switched to ES2015 output by default. This surfaces an issue with the Angular compiler where use of forwardRef results in broken code.

Say you have some source code like this:

@Component({...})
export class MatCalendarHeader {
  constructor(@Inject(forwardRef(() => MatCalendar)) calendar: MatCalendar, ...) { ... }
}

@Component({...})
export class MatCalendar { ... }

With ES5, the JS output for this will look something like:

    var MatCalendarHeader = /** @class */ (function () {
        function MatCalendarHeader(calendar, _dateAdapter, _dateFormats, changeDetectorRef) {
          /** ... */
        }
        /** ... */
        MatCalendarHeader = tslib_1.__decorate([
            core_1.Component({ /** ... */ }),
            tslib_1.__param(1, core_1.Inject(core_1.forwardRef(function () { return MatCalendar; }))),
            tslib_1.__param(2, core_1.Optional()),
            tslib_1.__param(3, core_1.Optional()), tslib_1.__param(3, core_1.Inject(core_2.MAT_DATE_FORMATS)),
            tslib_1.__metadata("design:paramtypes", [datepicker_intl_1.MatDatepickerIntl,
                MatCalendar,
                core_2.DateAdapter, Object, core_1.ChangeDetectorRef])
        ], MatCalendarHeader);
        return MatCalendarHeader;
    }());
    exports.MatCalendarHeader = MatCalendarHeader;

    var MatCalendar = /** @class */ (function () {
        function MatCalendar(/** ... */) { }
        /** ... */
        return MatCalendar;
    }());

Notice a few key things:

  1. MatCalendar is referenced in the __decorate block of MatCalendarHeader because it's used in the type position for a constructor param
  2. MatCalendar is declared after MatCalendarHeader
  3. MatCalendar is declared with var.

With the ES2015 output, the result is mostly the same, except that the use of var is now let. This causes a run-time error because the value of MatCalendar is read before it is initialized.

The workaround for this it to modify the tsconfig used by bazel to include

  "bazelOptions": {
    "devmodeTargetOverride": "es5"
  }

Activity

  1. devversion commented on Mar 5, 2019

    @devversion
    Member

    Just for later reference: I think this only surfaces within Bazel as the Bazel & Angular setup by default does not use tsickle, while usually developers build Angular apps with tsickle which:

    1. Removes the TypeScript generated metadata (e.g. the __decorate calls)
    2. Tsickle wraps the metadata retrieval in a closure. Meaning that it works like kind of like forwardRef
  2. added
    area: bazelIssues related to the published `@angular/bazel` build rules
    on Mar 5, 2019
  3. added this to the needsTriage milestone on Mar 5, 2019
  4. modified the milestones: needsTriage, Backlog on Apr 24, 2019
  5. alexeagle commented on Apr 25, 2019

    @alexeagle
    Contributor

    We removed tsickle from the default compiler setup, so now this is an issue for all developers under differential loading. Raising frequency.

  6. alexeagle commented on May 9, 2019

    @alexeagle
    Contributor

    let's track it in the duplicate #30106

  7. angular-automatic-lock-bot commented on Sep 15, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  8. locked and limited conversation to collaborators on Sep 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions