diff --git a/packages/core/src/render3/metadata.ts b/packages/core/src/render3/metadata.ts index 053d742845..e1e1974588 100644 --- a/packages/core/src/render3/metadata.ts +++ b/packages/core/src/render3/metadata.ts @@ -27,8 +27,17 @@ export function setClassMetadata( type: Type, decorators: any[] | null, ctorParameters: (() => any[]) | null, propDecorators: {[field: string]: any} | null): void { const clazz = type as TypeWithMetadata; + + // We determine whether a class has its own metadata by taking the metadata from the parent + // constructor and checking whether it's the same as the subclass metadata below. We can't use + // `hasOwnProperty` here because it doesn't work correctly in IE10 for static fields that are + // defined by TS. See https://github.com/angular/angular/pull/28439#issuecomment-459349218. + const parentPrototype = clazz.prototype ? Object.getPrototypeOf(clazz.prototype) : null; + const parentConstructor: TypeWithMetadata|null = parentPrototype && parentPrototype.constructor; + if (decorators !== null) { - if (clazz.hasOwnProperty('decorators') && clazz.decorators !== undefined) { + if (clazz.decorators !== undefined && + (!parentConstructor || parentConstructor.decorators !== clazz.decorators)) { clazz.decorators.push(...decorators); } else { clazz.decorators = decorators; @@ -45,7 +54,8 @@ export function setClassMetadata( // decorator types. Decorators on individual fields are not merged, as it's also incredibly // unlikely that a field will be decorated both with an Angular decorator and a non-Angular // decorator that's also been downleveled. - if (clazz.propDecorators !== undefined) { + if (clazz.propDecorators !== undefined && + (!parentConstructor || parentConstructor.propDecorators !== clazz.propDecorators)) { clazz.propDecorators = {...clazz.propDecorators, ...propDecorators}; } else { clazz.propDecorators = propDecorators; diff --git a/packages/core/test/render3/jit/directive_spec.ts b/packages/core/test/render3/jit/directive_spec.ts index c81842c475..92e214afb7 100644 --- a/packages/core/test/render3/jit/directive_spec.ts +++ b/packages/core/test/render3/jit/directive_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, HostListener} from '@angular/core'; +import {Directive, HostListener, Input} from '@angular/core'; import {setClassMetadata} from '@angular/core/src/render3/metadata'; import {convertToR3QueryMetadata, directiveMetadata, extendsDirectlyFromObject} from '../../../src/render3/jit/directive'; @@ -130,5 +130,29 @@ describe('jit directive helper functions', () => { expect(directiveMetadata(SuperDirective, {}).propMetadata.handleClick).toBeFalsy(); expect(directiveMetadata(SubDirective, {}).propMetadata.handleClick).toBeFalsy(); }); + + it('should not inherit propMetadata from super class when sub class has its own propMetadata', + () => { + class SuperDirective {} + setClassMetadata(SuperDirective, [{type: Directive}], null, { + someInput: [{type: Input}], + handleClick: [{type: HostListener, args: ['click', ['$event']]}] + }); + + class SubDirective extends SuperDirective {} + setClassMetadata( + SubDirective, [{type: Directive}], null, {someOtherInput: [{type: Input}]}); + + const superPropMetadata = directiveMetadata(SuperDirective, {}).propMetadata; + const subPropMetadata = directiveMetadata(SubDirective, {}).propMetadata; + + expect(superPropMetadata.handleClick).toBeTruthy(); + expect(superPropMetadata.someInput).toBeTruthy(); + + expect(subPropMetadata.handleClick).toBeFalsy(); + expect(subPropMetadata.someInput).toBeFalsy(); + expect(subPropMetadata.someOtherInput).toBeTruthy(); + }); + }); });