-
Notifications
You must be signed in to change notification settings - Fork 25.5k
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
forwardRef breaks at runtime with ES2015 #30106
Comments
This sounds like something that Angular simply cannot fix since it's a limitation of how JS works, right? |
I think (in v8) at runtime, a lot of people may be suprised targeting es2015 by default due to the fact CLI historically allowed This will breaks a lot of existing angular project, for sure |
I've found a TypeScript issue regarding this: microsoft/TypeScript#27519 |
This seems to be the same as #29107, although that one was initially observed in Bazel toolchains only. |
well it breaks our application that's for sure, on multiple places ;) |
@LanderBeeuwsaert we actually fixed this on angular/angular-cli#14473. Or at least we thought we did. Can you tell us more about what you are seeing? A repro would be great too. cc @clydin |
@filipesilva Certainly also because my guess is that it's related to some circular dependency somewhere and we have a lot of them. (the blocking ones we solve by combinations of forwardRef and using Injector.get()) If I solve the first error, by changing the code in AppReadyEvent service, then the issue appears in a component. So I actually guess that's progress ;). And, now it seems to not be related to forwardRef: Ha, I'm updating this as I go along :), So I was thinking that it could be related to the fact that I give the SheetViewComponent as type, in a component that SheetViewComponent is connected to already. (so they are circularely referencing each other through multiple other components in between). So the change from 7 to 8 seems to be that somehow inputs cannot have circular references? |
New update. Everytime I solve one of these, a new (similar) issue pops up. So for me, there seems to be a real consistent change in the way forwardRef/Input usage of types are compiled/handled. Seems that it's maybe related to that it sees input() types also in compiling/referencing of the types? I'm pretty sure that if I would put any as type instead of SheetViewComponent, it would run without failing. Do you want me to try? (but of course as a permanent solution I would prefer not to have to change everything to type any.) |
Extra update, I do have to say that in polyfills.ts I had to do: as compilation didn't find the es6/reflect ; es7/reflect so maybe could be related to that? |
As workaround, I think you can have both type |
@LanderBeeuwsaert it isn't as much ForwardRef/Input itself, as much as it is differential loading. The problems comes around with circular dependencies when using ES2015. And using differential loading (which happens automatically on the update to 8) uses ES2015 by default. In https://angular.io/guide/deployment#opting-out-of-differential-loading you can find more information about opting out. For you, the right option is This isn't great but at least you can go back to a stable usage with this single switch, and still use the rest of Angular 8 while you look at addressing the circular dependencies. |
Thanks @istiti , good idea, I'll wait for now, see if the issue gets resolved during the coming weeks. If not we'll weigh the investment of time. Anyway, crazy coincidence but seems that we have mutual connections. (we're in Genèva now for the GIAC) |
@filipesilva , ok, thanks for the solution. |
We thought we had taken care of it already, but it looks like we haven't :/ To be honest it's not 100% clear to me if the problem is exactly the same as forwardRef. I imagine it's still related to circular deps, but your problem might be a bit more involved. At the end of the day the circular dep thing comes down to microsoft/TypeScript#27519. In ES5, a circular dependency would not be a problem when loading modules, it would just undefined at some point. In ES2015 it will be an error at runtime. So I think that yes, if you want to use ES2015 you will need to break up circular dependencies at some point. There's a couple of good strategies to do it, as it is not a Angular specific thing. Since circular dependencies involving Types are a bit more frequent, they come up more often. In this particular case we cared a lot about forwardRef because it was supposed to get around this problem to begin with. But it's not the only case of circular deps. |
Ok, thanks for all the info, we'll let this sink in for a while. |
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce workarounds in the compiler derivatives (i.e. CLI and ng-packagr), and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce workarounds in the compiler derivatives (i.e. CLI and ng-packagr), and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce workarounds in the compiler derivatives (i.e. CLI and ng-packagr), and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce workarounds in the compiler derivatives (i.e. CLI and ng-packagr), and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce workarounds in the compiler derivatives (i.e. CLI and ng-packagr), and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce workarounds in the compiler derivatives (i.e. CLI and ng-packagr), and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters/ore removed decorators at all to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce code duplication in the compiler derivatives (e.g. ng-packagr), fixes the left-behind standalone ngc comsumers, and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters/ore removed decorators at all to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce code duplication in the compiler derivatives (e.g. ng-packagr), fixes the left-behind standalone ngc comsumers, and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters/ore removed decorators at all to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce code duplication in the compiler derivatives (e.g. ng-packagr), fixes the left-behind standalone ngc comsumers, and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR: (1): TypeScript has a limitation where `forwardRef` breaks in es2015 due to `forwardRef` calls being invoked immediately as part of decorator metadata. See: angular/angular-cli#14424 and angular#30106. (2): TypeScript preserves type information for class members, and references the type values immediately (as with `forwardRef` above). This means that using native DOM globals as property types could break SSR. This is because loading such JS file requires these DOM globals to exist in NodeJS globally too (and there is no support for DI mocking). See: angular#30586. This is especially relevant for libraries that do not want to use tsickle but ship SSR-compatible code. The root cause for (1) is a TypeScript limitation as mentioned. This is the related upstream ticket: microsoft/TypeScript#27519. Downleveling decorators to static properties fixes the issues, as outlined in (1) and (2), because we can defer metadata evaluation to avoid direct evaluation on file load. Additionally, we have more control and can discard unnnecessary metadata information, like class member types that are not needed by Angular at all see (2). One might wonder why this hasn't been an issue in the past since we disabled this as part of version 7. These issues didn't surface at a large scale because we added a custom transformer to CLI projects and to `ng-packagr`. Those transformers downleveled constructor parameters/ore removed decorators at all to fix (1) and (2). Also `emitMetadataDecorator` has been disabled by default in CLI projects. For bazel release output this didn't surface either because tsickle still ran by default in prodmode output. This was never an ideal solution though, and we'd also like to not run tsickle by default in the Bazel prodmode output. It was not ideal because we just applied workarounds at Angular compiler derivatives. Ideally, TypeScript would just emit proper metadata that isn't evaluated at top-level, but given they marked it as limitation and the decorator proposal is still stage 2, this won't happen any time soon (if at all). The ideal solution is that we downlevel decorators (as previously done with tsickle by default) as part of the Angular compiler (a level higher; and one below the actual TypeScript compiler limitation). This fixes the issues with the common `forwardRef` pattern (1), and also fixes (2). It also allows us to reduce code duplication in the compiler derivatives (e.g. ng-packagr), fixes the left-behind standalone ngc comsumers, and we can disable tsickle in Angular bazel (as already done with this commit). Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199.
…37382) In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: #30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes #30106. Fixes #30586. Fixes #30141. Resolves FW-2196. Resolves FW-2199. PR Close #37382
…ngular#37382) In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199. PR Close angular#37382
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
…ngular#37382) In v7 of Angular we removed `tsickle` from the default `ngc` pipeline. This had the negative potential of breaking ES2015 output and SSR due to a limitation in TypeScript. TypeScript by default preserves type information for decorated constructor parameters when `emitDecoratorMetadata` is enabled. For example, consider this snippet below: ``` @directive() export class MyDirective { constructor(button: MyButton) {} } export class MyButton {} ``` TypeScript would generate metadata for the `MyDirective` class it has a decorator applied. This metadata would be needed in JIT mode, or for libraries that provide `MyDirective` through NPM. The metadata would look as followed: ``` let MyDirective = class MyDir {} MyDirective = __decorate([ Directive(), __metadata("design:paramtypes", [MyButton]), ], MyDirective); let MyButton = class MyButton {} ``` Notice that TypeScript generated calls to `__decorate` and `__metadata`. These calls are needed so that the Angular compiler is able to determine whether `MyDirective` is actually an directive, and what types are needed for dependency injection. The limitation surfaces in this concrete example because `MyButton` is declared after the `__metadata(..)` call, while `__metadata` actually directly references `MyButton`. This is illegal though because `MyButton` has not been declared at this point. This is due to the so-called temporal dead zone in JavaScript. Errors like followed will be reported at runtime when such file/code evaluates: ``` Uncaught ReferenceError: Cannot access 'MyButton' before initialization ``` As noted, this is a TypeScript limitation because ideally TypeScript shouldn't evaluate `__metadata`/reference `MyButton` immediately. Instead, it should defer the reference until `MyButton` is actually declared. This limitation will not be fixed by the TypeScript team though because it's a limitation as per current design and they will only revisit this once the tc39 decorator proposal is finalized (currently stage-2 at time of writing). Given this wontfix on the TypeScript side, and our heavy reliance on this metadata in libraries (and for JIT mode), we intend to fix this from within the Angular compiler by downleveling decorators to static properties that don't need to evaluate directly. For example: ``` MyDirective.ctorParameters = () => [MyButton]; ``` With this snippet above, `MyButton` is not referenced directly. Only lazily when the Angular runtime needs it. This mitigates the temporal dead zone issue caused by a limitation in TypeScript's decorator metadata output. See: microsoft/TypeScript#27519. In the past (as noted; before version 7), the Angular compiler by default used tsickle that already performed this transformation. We moved the transformation to the CLI for JIT and `ng-packager`, but now we realize that we can move this all to a single place in the compiler so that standalone ngc consumers can benefit too, and that we can disable tsickle in our Bazel `ngc-wrapped` pipeline (that currently still relies on tsickle to perform this decorator processing). This transformation also has another positive side-effect of making Angular application/library code more compatible with server-side rendering. In principle, TypeScript would also preserve type information for decorated class members (similar to how it did that for constructor parameters) at runtime. This becomes an issue when your application relies on native DOM globals for decorated class member types. e.g. ``` @input() panelElement: HTMLElement; ``` Your application code would then reference `HTMLElement` directly whenever the source file is loaded in NodeJS for SSR. `HTMLElement` does not exist on the server though, so that will become an invalid reference. One could work around this by providing global mocks for these DOM symbols, but that doesn't match up with other places where dependency injection is used for mocking DOM/browser specific symbols. More context in this issue: angular#30586. The TL;DR here is that the Angular compiler does not care about types for these class members, so it won't ever reference `HTMLElement` at runtime. Fixes angular#30106. Fixes angular#30586. Fixes angular#30141. Resolves FW-2196. Resolves FW-2199. PR Close angular#37382
🐞 bug report
Affected Package
The issue is caused by package @angular/coreIs this a regression?
No.Description
Using
forwardRef
as described in https://angular.io/api/core/forwardRef while targeting ES2015 results on aUncaught ReferenceError: Cannot access 'Lock' before initialization
runtime error.🔬 Minimal Reproduction
ng new forward-ref-project && cd forward-ref-project
tsconfig.json
contains"target": "es2015",
src/main.ts
with:ng serve -o
🔥 Exception or Error
🌍 Your Environment
Angular Version:
Anything else relevant?
forwardRef
is provided specifically for the purpose of referencing something that isn't defined. This is useful in breaking circular dependencies, and when declaring both services and components in the same file.forwardRef
works because it delays the resolution of the reference to a time at which it is already declared through the callback indirection. In the API example, the symbol we want to delay resolution isLock
:But
Lock
is actually being referenced in more places than just insideforwardRef
. It is also being used as a TS type in the class property, and in the constructor parameter.Types don't usually have a runtime representation so that shouldn't be a problem. But constructor types are an exception and actually do have a runtime representation. We can see this by looking at the transpiled code:
The
Lock
type in the for the constructor parameter was transpiled intotslib_1.__metadata("design:paramtypes", [Lock])
. This reference does not have a delayed resolution like the injectedforwardRef
and is instead immediately resolved, resulting inUncaught ReferenceError: Cannot access 'Lock' before initialization
.This error isn't observed when targetting ES5 however. We can understand why by looking at the code when transpiled to ES5 :
In ES5 there are no
class
declarations, so TS instead uses avar
. One important different betweenvar
andclass/let/const
is that the latter are all subject to the Temporal Dead Zone.In practical terms the TDZ means that using a
var
before it is declared resolves toundefined
, but using aclass/let/const
instead throws aReferenceError
. This is the error we are seeing here.A possible workaround is to not declare the type in the constructor:
This will change the transpiled code and remove the reference, avoiding the
ReferenceError
:One important note is that the
ReferenceError
does not come up on Angular CLI projects compiled with AOT. This is because there we actually transform transpiled TS code and remove Angular decorators, so the metadata reference (tslib_1.__metadata("design:paramtypes", [Lock])
) never reaches the browser and thus there is noReferenceError
.The text was updated successfully, but these errors were encountered: