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

Design:type metadata for cyclic dependencies throw at runtime #27519

Closed
bajtos opened this issue Oct 3, 2018 · 15 comments
Closed

Design:type metadata for cyclic dependencies throw at runtime #27519

bajtos opened this issue Oct 3, 2018 · 15 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@bajtos
Copy link

bajtos commented Oct 3, 2018

TypeScript Version: 3.2.0-dev.20181003

Search Terms: design cyclic

Code

index.ts

import 'reflect-metadata';

function property() {
  return (target: Object, key: string) => {
    const t = Reflect.getMetadata('design:type', target, key);
    console.log('property %s#%s of type', target.constructor.name, key, t.name);
  }
}

class Product {
  // belongs to a category
  @property()
  category: Category;
}

class Category {
  // has many products
  @property()
  products: Product[];
}

tsconfig.json

{
  "$schema": "http://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,

    "lib": ["es2018", "dom"],
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es2017"
  },
  "include": [
    "index.ts"
  ]
}

package.json

{
  "dependencies": {
    "reflect-metadata": "^0.1.12",
    "typescript": "^3.2.0-dev.20181003"
  }
}

Expected behavior:

Ideally, the code compiles and node index.js produces the following console output:

property Product#category of type Category
property Category#products of type Array

If this is not possible, then the compiler should detect cyclic dependencies and fail the compilation with a helpful error.

Actual behavior:

The code compiles. When the compiled code is executed, it fails at runtime.

index.js:23
    __metadata("design:type", Category)
                              ^

ReferenceError: Category is not defined
    at Object.<anonymous> (index.js:23:31)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    (...)

Here is the relevant snippet from the transpiled output:

class Product {
}
__decorate([
    property(),
    __metadata("design:type", Category)
], Product.prototype, "category", void 0);

class Category {
}
__decorate([
    property(),
    __metadata("design:type", Array)
], Category.prototype, "products", void 0);

Playground Link:

Playground does not support emitDecoratorMetadata and experimentalDecorators options ☹️

Related Issues:

None found.

@bajtos
Copy link
Author

bajtos commented Oct 3, 2018

A possible solution I see is to modify the code setting design-type metadata to use a resolver/getter function instead of passing the type constructor directly, and access design-type metadata a bit later, after all classes were defined. I guess such requirement makes my proposed solution rather impractical.

function property() {
    return (target, key) => {
      // Typically, users won't call process.nextTick but change the decorator to only store
      // design-type getter function. The actual type class will be read by other code at time 
      // that may be still in the same tick, but later enough to have all classes already defined
      process.nextTick(() => {
        const t = Reflect.getMetadata('design:type', target, key) || (() => {});
        console.log('property %s %s of type', target.constructor.name, key, t().name);
      });
    };
}
class Product {
}
__decorate([
    property(),
    __metadata("design:type", () => Category)
], Product.prototype, "category", void 0);
class Category {
}
__decorate([
    property(),
    __metadata("design:type", () => Array)
], Category.prototype, "products", void 0);

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Oct 3, 2018
@bajtos
Copy link
Author

bajtos commented Oct 4, 2018

RyanCavanaugh added the Design Limitation label 13 hours ago

@RyanCavanaugh Thank you for taking look at this issue. The description of Design Limitation label says: Constraints of the existing architecture prevent this from being fixed. I understand it may not be possible to make my code snippet shown above to work, but cannot we at least improve the compiler to detect the problem at compile time please? Is such improvement prevented by limitations of the existing architecture too?

@dawidgarus
Copy link

TypeScript compile could produce code that used forwardRef, similar how Angular suggest: https://angular.io/api/core/forwardRef
Note that the Angular example doesn't work when targeting esnext precisely because of this issue.
The compiler could produce:

__metadata("design:type", __forwardRef(() => Category))

It also applies to design:paramtypes.

@filipesilva
Copy link
Contributor

I was looking at this again today and @alxhub brought up an interesting point: TS will emit invalid ES2015 code for valid TS regardless of circular dependencies.

Consider this code:

function decorator(target) {}

// Error at compile time: error TS2449: Class 'Lock' used before its declaration.
// console.log(Lock);

@decorator
class Door {
  // No error when using Lock as a type, but will error at runtime
  //  __metadata("design:paramtypes", [Lock])
  //                                   ^
  // ReferenceError: Lock is not defined
  constructor(lock: Lock) { }
}

class Lock { }

Using TS 3.4.5 and NodeJS v10.10.0, you can compile and run it via npx tsc --target ES2015 --experimentalDecorators --emitDecoratorMetadata ./main.ts && node ./main.js.

The TS compilation will succeed but execution will fail:

kamik@RED-X1C6 MINGW64 /d/sandbox/rc-project/test (master)
$ npx tsc --target ES2015 --experimentalDecorators --emitDecoratorMetadata ./main.ts && node main.js
D:\sandbox\rc-project\test\main.js:22
    __metadata("design:paramtypes", [Lock])
                                     ^

ReferenceError: Lock is not defined
    at Object.<anonymous> (D:\sandbox\rc-project\test\main.js:22:38)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
    at startup (internal/bootstrap/node.js:279:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:696:3)

Yet there would be a compilation error if Lock was to be used as a value and not a type.

Perhaps there should be a compilation error for Types being used before being declared, at least if emitDecoratorMetadata is turned on. That, or it shouldn't emit broken code.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Apr 30, 2019

I think this issue has been discussed and is indeed a design limitation - pinging @rbuckton for context.

@bajtos
Copy link
Author

bajtos commented Apr 30, 2019

I think this issue has been discussed and is indeed a design limitation

Personally, I can live with with the limitation, i.e. that TypeScript cannot emit design-type metadata when the property is using a type before it's declared.

However, I'd like the problem to be detected and reported by the TypeScript compiler, the compiler should fail the build with a descriptive error message that will make it easy for the developer to understand what they did wrong and how to fix the problem.

Compare it with the current situation, where compiler happily produces JavaScript code and the problem is discovered only at runtime, usually with a hard crash on uncaught ReferenceError. I find such behavior rather impractical - what is the compiler good for when it cannot detect incorrect code? The problem can be also difficult to diagnose and troubleshoot, especially for people not aware of this limitation. The message "ReferenceError: Lock is not defined" with a stack trace pointing somewhere deep into code added by the compiler provide very little hints on how to fix the problem.

@waterplea
Copy link

waterplea commented Jun 11, 2019

That, or it shouldn't emit broken code.

Maybe you could add a warning that broken code was produced and remove it? After all, it is a metadata issue and the code itself is working. I came here from Angular, where metadata are only kept in dev mode, in production it works fine, but when I try to fire it up locally with JIT compiler — I get an error.

@spion
Copy link

spion commented Sep 20, 2019

@DanielRosenwasser do you think there is a way to move forward to introduce a forwardRef based implementation? Based on the above info it doesn't look like it would suffer from the issue. Of course it still wont work for constructor-based DI, but for lazy property based injection or for describing other relationships between classes it would work.

IMO this one might be worth the extra effort as its causing rippling effects in the new emergent TS ecosystem (angular, inversify/nestjs etc).

(In Inversify for instance, a type-unsafe token based alternative is recommended instead of forwardRef)

@DanielRosenwasser
Copy link
Member

I'll try to keep it in mind, but I really don't think I can reliably give an answer in the near future until I can focus on decorators again. Recently my standards focus has been on optional chaining, nullish coalescing, and class fields.

@rbuckton
Copy link
Member

@spion Any change we might make would be a breaking change. We considered switching to a new metadata format some time ago, as there is no way to handle forward references in a non-breaking way with the current emit. Something like this:

class Foo {
  // @__metadata("design:paramtypes", [Number, String])
  // @__metadata("design:returntype", Bar) // error
  @__metadata("design:typeinfo", {
    paramTypes: () => [Number, String], // deferred via arrow, no error
    returnType: () => Bar, // deferred via arrow, no error
  })
  method(x, y) {
    return new Bar(x, y);
  }
}
class Bar { ... }

We held off on any significant changes to this behavior as there are other issues limiting its utility:

  • No way to represent interfaces
  • No way to represent generics
  • No way to represent function/constructor types
  • No way to represent other complex types (tuples, arrays, conditional types, etc.)

With the decorators proposal still in flux, we have not prioritized any changes to decorator emit (including metadata) until the proposal progresses further along the standards track.

@spion
Copy link

spion commented Sep 22, 2019

Got it - I guess the new decorator proposal seems like a good time to revisit.

FWIW when we want run-time interface representation we currently use "classterfaces" i.e. interfaces defined as a class

class IMyInterface {
  prop!: number;
  method!: (arg: number) => number
}

They seem to work just fine as a substitute for interfaces:

class AddFive implements IMyInterface {
  prop = 5
  method(arg: number) { return this.prop + arg; }
}

and its easy to decorate them with plenty of additional info and/or use them as type-safe DI tokens.

@bajtos
Copy link
Author

bajtos commented Sep 27, 2019

Any change we might make would be a breaking change.
We held off on any significant changes to this behavior as there are other issues limiting its utility.

Makes sense 👍

In which case: Can we please improve the compiler to detect the situation when it's going to emit invalid code that will later crash at runtime?

Would that be considering a breaking change? Since the emitted code is going to crash at runtime anyways, if the compiler decide to fail the build instead of emitting a code that will crash, then I would expect that such change should not break any valid/working applications out there.

Thoughts?

@spion
Copy link

spion commented Sep 30, 2019

@bajtos the problem is that any decorator causes an emit of metadata, but not all of them read it. This will cause non-related decorators to also crash.

@dr0p
Copy link

dr0p commented Mar 23, 2020

In our project we are using quite extensively the reflection API - it is core component of the serialization/deserialization in out codebase. We are currently experimenting with the folowing solution/workaround:

// ########## in Serialized.ts
const createProxyTypeConstructor = (getType: () => GenericConstructor<unknown>) => {
  const proxyTypeConstructor = class ProxyTypeConstructor {
    public constructor(...args: any) {
      return new (getType())(...args);
    }
  };

  Promise.resolve().then(() => {
    Object.setPrototypeOf(proxyTypeConstructor, getType());
  });

  return proxyTypeConstructor;
};

export class Serialized<SerializedType> {
  public static readonly metadata = {
    type: (callback: () => GenericConstructor<unknown>) => (target: any, propertyKey: string) => {
      Reflect.metadata('design:type', createProxyTypeConstructor(callback))(target, propertyKey);
    }
  };

  // serialization implementation and static utility members irrelevant to the reflection API
}
// ######## in topology.ts
export class Zone extends SerializedWithDirtyTracking<Breach.API.Topology.Zone> {
  // ...irrelevant members
  @Serialized.metadata.type(() => Topology)
  @observable
  public parent: InstanceType<typeof Topology>;
  // ...irrelevant members
}

// ...snip...

export class Topology extends Entity {
  @observable public zones = observable<{ [id: string]: Zone }>({});
}

This approach is mostly backward compatible:

  • By typing the member as InstanceType<typeof T> we are indirectly referring to the correct type/class/constructor, but we are forcing tsc to emit a dummy/generic metadata type - Object. This metadata is overriden by the @Serialized.metadata.type decorator.
  • The returned design:type for the member is an instantiable class that will result in object of the correct class. The prototype chain is extended with a single link so .isPrototypeOf checks will work as expected. Only direct prototype comparison will not work the same as with normal metadata, but this is a bad idea anyway..

This haven't been still tested thoroughly. I am open to any criticism/corrections/suggestions

I can open a PR to implement this behavior in tsc, but this will have to go as an alternative method of metadata decoration as it is not 100% compatible with the current implementation.

IgorMinar added a commit to IgorMinar/angular that referenced this issue May 27, 2020
In angular#37221 we disabled tsickle passes from transforming the tsc output that is used to publish all
Angular framework and components packages (@angular/*).

This change however revealed a bug in the ngc that caused __decorate and __metadata calls to still
be emitted in the JS code even though we don't depend on them.

Additionally it was these calls that caused code in @angular/material packages to fail at runtime
due to circular dependency in the emitted decorator code documeted as
microsoft/TypeScript#27519.

This change partially rolls back angular#37221 by reenabling the decorator to static fields (static
properties) downleveling.

This is just a temporary workaround while we are also fixing root cause in `ngc` - tracked as
FW-2199.

Resolves FW-2198.
Related to FW-2196
devversion added a commit to devversion/angular that referenced this issue Jun 2, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 2, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 2, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 2, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 2, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 2, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 5, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 5, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 5, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 5, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 6, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 9, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 9, 2020
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.
devversion added a commit to devversion/angular that referenced this issue Jun 9, 2020
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.
atscott pushed a commit to angular/angular that referenced this issue Jun 10, 2020
…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
atscott pushed a commit to angular/angular that referenced this issue Jun 10, 2020
…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
ngwattcos pushed a commit to ngwattcos/angular that referenced this issue Jun 25, 2020
…ngular#37317)

In angular#37221 we disabled tsickle passes from transforming the tsc output that is used to publish all
Angular framework and components packages (@angular/*).

This change however revealed a bug in the ngc that caused __decorate and __metadata calls to still
be emitted in the JS code even though we don't depend on them.

Additionally it was these calls that caused code in @angular/material packages to fail at runtime
due to circular dependency in the emitted decorator code documeted as
microsoft/TypeScript#27519.

This change partially rolls back angular#37221 by reenabling the decorator to static fields (static
properties) downleveling.

This is just a temporary workaround while we are also fixing root cause in `ngc` - tracked as
FW-2199.

Resolves FW-2198.
Related to FW-2196

PR Close angular#37317
ngwattcos pushed a commit to ngwattcos/angular that referenced this issue Jun 25, 2020
…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
profanis pushed a commit to profanis/angular that referenced this issue Sep 5, 2020
…ngular#37317)

In angular#37221 we disabled tsickle passes from transforming the tsc output that is used to publish all
Angular framework and components packages (@angular/*).

This change however revealed a bug in the ngc that caused __decorate and __metadata calls to still
be emitted in the JS code even though we don't depend on them.

Additionally it was these calls that caused code in @angular/material packages to fail at runtime
due to circular dependency in the emitted decorator code documeted as
microsoft/TypeScript#27519.

This change partially rolls back angular#37221 by reenabling the decorator to static fields (static
properties) downleveling.

This is just a temporary workaround while we are also fixing root cause in `ngc` - tracked as
FW-2199.

Resolves FW-2198.
Related to FW-2196

PR Close angular#37317
profanis pushed a commit to profanis/angular that referenced this issue Sep 5, 2020
…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
@paeolo
Copy link

paeolo commented Oct 23, 2020

@rbuckton Any update on this?

I don't really understand the reason why typescript emit "Type" instead of "() => Type" since the latter resolves some cyclic dependencies problems.

To me it sounds like a tiny change in an experimental feature anyway, and this tiny change would improve a lot of TS projects, and of course typescript itself.

Debugging circular import problem, when one is not aware of theses subtleties, is very hard.

Note: I spent some time yesterday making a minimal reproducible code and describing the issue in clear term here: #41201.

thewilkybarkid added a commit to PREreview/prereview that referenced this issue Nov 25, 2021
Decorators are an experimental feature in TypeScript, which is known to have issues. Changes in Parcel means that it can no longer build the app, so their use is blocking updating Parcel to a stable release.

This change removes the ORM decorators in favour of defining entities programmatically. I'm hoping that the tests and migration-creating script are enough to have caught any mistakes, but given that areas of the app remain untested, it's hard to be sure.

Also, this change has to disable Parcel's scope hoisting, as it renames the entity classes. The rename wasn't a problem when using decorators as the generated code saw the class name left unchanged. Now, however, the ORM breaks if they are changed.

Refs #399, parcel-bundler/parcel#7293 (comment), microsoft/TypeScript#27519
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

10 participants