feat(core): properly support inheritance
## Inheritance Semantics: Decorators: 1) list the decorators of the class and its parents in the ancestor first order 2) only use the last decorator of each kind (e.g. @Component / ...) Constructor parameters: If a class inherits from a parent class and does not declare a constructor, it inherits the parent class constructor, and with it the parameter metadata of that parent class. Lifecycle hooks: Follow the normal class inheritance model, i.e. lifecycle hooks of parent classes will be called even if the method is not overwritten in the child class. ## Example E.g. the following is a valid use of inheritance and it will also inherit all metadata: ``` @Directive({selector: 'someDir'}) class ParentDirective { constructor(someDep: SomeDep) {} ngOnInit() {} } class ChildDirective extends ParentDirective {} ``` Closes #11606 Closes #12892
This commit is contained in:
@ -93,6 +93,15 @@ export class MetadataCollector {
|
||||
}
|
||||
}
|
||||
|
||||
// Add class parents
|
||||
if (classDeclaration.heritageClauses) {
|
||||
classDeclaration.heritageClauses.forEach((hc) => {
|
||||
if (hc.token === ts.SyntaxKind.ExtendsKeyword && hc.types) {
|
||||
hc.types.forEach(type => result.extends = referenceFrom(type.expression));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add class decorators
|
||||
if (classDeclaration.decorators) {
|
||||
result.decorators = getDecorators(classDeclaration.decorators);
|
||||
@ -196,8 +205,7 @@ export class MetadataCollector {
|
||||
result.statics = statics;
|
||||
}
|
||||
|
||||
return result.decorators || members || statics ? recordEntry(result, classDeclaration) :
|
||||
undefined;
|
||||
return recordEntry(result, classDeclaration);
|
||||
}
|
||||
|
||||
// Predeclare classes and functions
|
||||
@ -257,11 +265,7 @@ export class MetadataCollector {
|
||||
const className = classDeclaration.name.text;
|
||||
if (node.flags & ts.NodeFlags.Export) {
|
||||
if (!metadata) metadata = {};
|
||||
if (classDeclaration.decorators) {
|
||||
metadata[className] = classMetadataOf(classDeclaration);
|
||||
} else {
|
||||
metadata[className] = {__symbolic: 'class'};
|
||||
}
|
||||
metadata[className] = classMetadataOf(classDeclaration);
|
||||
}
|
||||
}
|
||||
// Otherwise don't record metadata for the class.
|
||||
@ -469,14 +473,15 @@ function validateMetadata(
|
||||
}
|
||||
}
|
||||
|
||||
function validateMember(member: MemberMetadata) {
|
||||
function validateMember(classData: ClassMetadata, member: MemberMetadata) {
|
||||
if (member.decorators) {
|
||||
member.decorators.forEach(validateExpression);
|
||||
}
|
||||
if (isMethodMetadata(member) && member.parameterDecorators) {
|
||||
member.parameterDecorators.forEach(validateExpression);
|
||||
}
|
||||
if (isConstructorMetadata(member) && member.parameters) {
|
||||
// Only validate parameters of classes for which we know that are used with our DI
|
||||
if (classData.decorators && isConstructorMetadata(member) && member.parameters) {
|
||||
member.parameters.forEach(validateExpression);
|
||||
}
|
||||
}
|
||||
@ -487,7 +492,7 @@ function validateMetadata(
|
||||
}
|
||||
if (classData.members) {
|
||||
Object.getOwnPropertyNames(classData.members)
|
||||
.forEach(name => classData.members[name].forEach(validateMember));
|
||||
.forEach(name => classData.members[name].forEach((m) => validateMember(classData, m)));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user