-
Notifications
You must be signed in to change notification settings - Fork 108
Only emit ctorParameters
when the class has a constructor
#760
Only emit ctorParameters
when the class has a constructor
#760
Conversation
@deadalusai can you share what problem exactly this fixes? I'm not sure if we have code (e.g. in Angular) that might depend on these empty constructors params being produced. Evan might know. |
Hi Martin. Sorry for the curt PR - I meant to have a PR up for @angular/angular with links for context, but ran out of time yesterday. I'm working to resolve an issue I found compiling though ng-packagr, where the constructor function generated for a subclass with an implicit constructor does not play nicely with the regex used to detect such a thing in Angular's ReflectionCapabilities._ownParameters. Specifically, function FooComponent () {
__super.apply(this, __tslib.__spread(arguments));
} Angular is expecting something like: function FooComponent () {
__super.apply(this, arguments);
}
The problem is that the work-around is currently the "official" way to detect this edge case. My goal here is to bring // Pseudocode
function getConstructorParams (typeConstructor) {
// legacy regex check, for old versions of tsc
if (DELEGATE_CTOR.test ... etc) {
return null;
}
// check for tsickle metadata
if (typeConstructor.ctorParameters) {
return typeConstructor.ctorParameters;
}
let metadata = this._reflect.getOwnMetadata('design:paramtypes', typeConstructor);
if (metadata) {
return metadata;
}
// pass-through constructor, or no metadata available
return null;
}
// the type either has a real constructor...
let parameters = getConstructorParams(constructorType);
if (!parameters && parentType !== Object) {
// ...or we should use the parent constructor
parameters = getConstructorParams(parentType);
}
return parameters || []; The regex would be left behind as a legacy check to support code generated with old versions of (The regex check has already grown from one to three different regexes in the last few minor versions of Angular) |
Just realized my examples above are missing the most important case of a subclass with an implicit constructor. Here's what @Injectable
class Base {
constructor (d: Dependency) {}
}
@Injectable
class Sub extends Base {
} Generating: var Base = /** @class */ (function () {
function Base(d) {
}
Base = __decorate([
Injectable,
__metadata("design:paramtypes", [Dependency])
], Base);
return Base;
}());
var Sub = /** @class */ (function (_super) {
__extends(Sub, _super);
function Sub() {
return _super !== null && _super.apply(this, arguments) || this;
}
Sub = __decorate([
Injectable
// NOTE: no "design:paramtypes" metadata for the subclass
], Sub);
return Sub;
}(Base)); I'd like tsickle to do the same. |
@mhevery can you figure out who's a good person to coordinate this change? I don't quite understand the interaction and timelines of the components involved. Can you assign a reviewer? |
The Angular PR can be found here: angular/angular#22642
Might have found a solution for the problem I had. Feedback welcome. |
So I looked into it, and the PRs seems reasonable, but it seems like we would have to coordinate angular/angular#22642 with this PR. This seems like an awful lot of work, along with supporting the legacy. Could we just fix ng-packagr/ng-packagr#679 to generate code which would pass current angular checks? |
I gave it some more thought and my recommendation would be to do nothing and wait for angular/angular#21706 to land which will make the whole |
@mhevery It’s only dropped in |
@trotyl I am not sure I understand your comment. Can you expand on it? |
@mhevery In Ivy the metadata constructor dependencies would be reflected in @Injectable()
class BaseComp {
constructor(myService: MyService) { }
}
@Component({ ... })
class MyComp extends BaseComp { } Would become: class MyComp extends BaseComp {
static ngComponentDef = defineComponent({
factory: () => new MyComp(inject(MyService))
})
} So the If we don't have RegExp match any more and without this change in tsickle, then: In AOT mode, nothing changed in metadata resolution since everything controlled by Imagine that the classes above comes from lib, with the format: class BaseComp { }
BaseComp.ctorParameters = () => [ { type: MyService } ]
class MyComp extends BaseComp { }
MyComp.ctorParameters = () => [] Then in a JIT'ed Ivy app: import { MyComp } from 'my-lib'
class CustomComp extends MyComp { } According to the inheritance semantics the deps for This case may only happen to back patch mode since I'm not sure how does pure Ivy lib looks like, metadata inheritance in Ivy seems not implemented yet, but for supporting JIT usage, there still needs to be record stands for |
Which (for an implicit constructor) should always be correct? This would eliminate the need to walk up the class hierarchy entirely. Unfortunately |
I see, thanks for the explanation, I see it better now. In which case I think I am for landing this. after angular/angular#22642 lands. |
Could we get this rebased on latest master? |
@mhevery I can rebase this today. Were you interested in this PR making
The behavior would be different to |
…s has a constructor This more closely matches the behavior of TSC which only emits the "design:paramtypes" metadata when the class actually has a constructor function.
Rebased 👍 |
Merged, thanks for the contribution. |
Fixes that the table does not render properly when used inside of a ES2015 application. This is due to an ongoing Angular issue which has been caused due to a brittle Regex check [introduced here](angular/angular#22356 (comment)) that should be replaced with a more clean approach for identifying classes that inherit metadata from another class. * angular/angular#22642 * angular/tsickle#760 Fixes angular#9329
Fixes that some components does not render properly when used inside of a ES2015 JIT application. This is due to an ongoing Angular issue which has been caused due to a brittle Regex check [introduced here](angular/angular#22356 (comment)) that should be replaced with a more clean approach for identifying classes that inherit metadata from another class. * angular/angular#22642 * angular/tsickle#760 See the more detailed issue here: angular#12760 Fixes angular#9329.
Fixes that some components does not render properly when used inside of a ES2015 JIT application. This is due to an ongoing Angular issue which has been caused due to a brittle Regex check [introduced here](angular/angular#22356 (comment)) that should be replaced with a more clean approach for identifying classes that inherit metadata from another class. * angular/angular#22642 * angular/tsickle#760 See the more detailed issue here: #12760 Fixes #9329.
Fixes that some components does not render properly when used inside of a ES2015 JIT application. This is due to an ongoing Angular issue which has been caused due to a brittle Regex check [introduced here](angular/angular#22356 (comment)) that should be replaced with a more clean approach for identifying classes that inherit metadata from another class. * angular/angular#22642 * angular/tsickle#760 See the more detailed issue here: #12760 Fixes #9329.
Fix DecoratorClassVisitor to only emit
ctorParameters
when the class has a constructor.This more closely matches the behavior of TSC which only emits the "design:paramtypes" metadata
when the class actually has a constructor. For example, compiling the following TypeScript snippet:
Emits this JavaScript: