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:
@ -6,7 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {makeParamDecorator} from '../util/decorators';
|
||||
import {makeDecorator, makeParamDecorator} from '../util/decorators';
|
||||
|
||||
|
||||
/**
|
||||
* Type of the Inject decorator / constructor function.
|
||||
@ -150,7 +151,7 @@ export interface Injectable {}
|
||||
* @stable
|
||||
* @Annotation
|
||||
*/
|
||||
export const Injectable: InjectableDecorator = makeParamDecorator('Injectable', []);
|
||||
export const Injectable: InjectableDecorator = <InjectableDecorator>makeDecorator('Injectable', []);
|
||||
|
||||
/**
|
||||
* Type of the Self decorator / constructor function.
|
||||
|
@ -12,6 +12,12 @@ import {Type} from '../type';
|
||||
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
||||
import {GetterFn, MethodFn, SetterFn} from './types';
|
||||
|
||||
/**
|
||||
* Attention: This regex has to hold even if the code is minified!
|
||||
*/
|
||||
export const DELEGATE_CTOR =
|
||||
/^function\s+\S+\(\)\s*{\s*("use strict";)?\s*(return\s+)?\S+\.apply\(this,\s*arguments\)/;
|
||||
|
||||
export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
private _reflect: any;
|
||||
|
||||
@ -49,15 +55,26 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
return result;
|
||||
}
|
||||
|
||||
parameters(type: Type<any>): any[][] {
|
||||
private _ownParameters(type: Type<any>, parentCtor: any): any[][] {
|
||||
// If we have no decorators, we only have function.length as metadata.
|
||||
// In that case, to detect whether a child class declared an own constructor or not,
|
||||
// we need to look inside of that constructor to check whether it is
|
||||
// just calling the parent.
|
||||
// This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
|
||||
// that sets 'design:paramtypes' to []
|
||||
// if a class inherits from another class but has no ctor declared itself.
|
||||
if (DELEGATE_CTOR.exec(type.toString())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prefer the direct API.
|
||||
if ((<any>type).parameters) {
|
||||
if ((<any>type).parameters && (<any>type).parameters !== parentCtor.parameters) {
|
||||
return (<any>type).parameters;
|
||||
}
|
||||
|
||||
// API of tsickle for lowering decorators to properties on the class.
|
||||
const tsickleCtorParams = (<any>type).ctorParameters;
|
||||
if (tsickleCtorParams) {
|
||||
if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) {
|
||||
// Newer tsickle uses a function closure
|
||||
// Retain the non-function case for compatibility with older tsickle
|
||||
const ctorParameters =
|
||||
@ -70,20 +87,35 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
}
|
||||
|
||||
// API for metadata created by invoking the decorators.
|
||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||
const paramAnnotations = this._reflect.getMetadata('parameters', type);
|
||||
const paramTypes = this._reflect.getMetadata('design:paramtypes', type);
|
||||
if (isPresent(this._reflect) && isPresent(this._reflect.getOwnMetadata)) {
|
||||
const paramAnnotations = this._reflect.getOwnMetadata('parameters', type);
|
||||
const paramTypes = this._reflect.getOwnMetadata('design:paramtypes', type);
|
||||
if (paramTypes || paramAnnotations) {
|
||||
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
||||
}
|
||||
}
|
||||
// The array has to be filled with `undefined` because holes would be skipped by `some`
|
||||
|
||||
// If a class has no decorators, at least create metadata
|
||||
// based on function.length.
|
||||
// Note: We know that this is a real constructor as we checked
|
||||
// the content of the constructor above.
|
||||
return new Array((<any>type.length)).fill(undefined);
|
||||
}
|
||||
|
||||
annotations(typeOrFunc: Type<any>): any[] {
|
||||
parameters(type: Type<any>): any[][] {
|
||||
// Note: only report metadata if we have at least one class decorator
|
||||
// to stay in sync with the static reflector.
|
||||
const parentCtor = Object.getPrototypeOf(type.prototype).constructor;
|
||||
let parameters = this._ownParameters(type, parentCtor);
|
||||
if (!parameters && parentCtor !== Object) {
|
||||
parameters = this.parameters(parentCtor);
|
||||
}
|
||||
return parameters || [];
|
||||
}
|
||||
|
||||
private _ownAnnotations(typeOrFunc: Type<any>, parentCtor: any): any[] {
|
||||
// Prefer the direct API.
|
||||
if ((<any>typeOrFunc).annotations) {
|
||||
if ((<any>typeOrFunc).annotations && (<any>typeOrFunc).annotations !== parentCtor.annotations) {
|
||||
let annotations = (<any>typeOrFunc).annotations;
|
||||
if (typeof annotations === 'function' && annotations.annotations) {
|
||||
annotations = annotations.annotations;
|
||||
@ -92,21 +124,27 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
}
|
||||
|
||||
// API of tsickle for lowering decorators to properties on the class.
|
||||
if ((<any>typeOrFunc).decorators) {
|
||||
if ((<any>typeOrFunc).decorators && (<any>typeOrFunc).decorators !== parentCtor.decorators) {
|
||||
return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators);
|
||||
}
|
||||
|
||||
// API for metadata created by invoking the decorators.
|
||||
if (this._reflect && this._reflect.getMetadata) {
|
||||
const annotations = this._reflect.getMetadata('annotations', typeOrFunc);
|
||||
if (annotations) return annotations;
|
||||
if (this._reflect && this._reflect.getOwnMetadata) {
|
||||
return this._reflect.getOwnMetadata('annotations', typeOrFunc);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||
annotations(typeOrFunc: Type<any>): any[] {
|
||||
const parentCtor = Object.getPrototypeOf(typeOrFunc.prototype).constructor;
|
||||
const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || [];
|
||||
const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : [];
|
||||
return parentAnnotations.concat(ownAnnotations);
|
||||
}
|
||||
|
||||
private _ownPropMetadata(typeOrFunc: any, parentCtor: any): {[key: string]: any[]} {
|
||||
// Prefer the direct API.
|
||||
if ((<any>typeOrFunc).propMetadata) {
|
||||
if ((<any>typeOrFunc).propMetadata &&
|
||||
(<any>typeOrFunc).propMetadata !== parentCtor.propMetadata) {
|
||||
let propMetadata = (<any>typeOrFunc).propMetadata;
|
||||
if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
|
||||
propMetadata = propMetadata.propMetadata;
|
||||
@ -115,7 +153,8 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
}
|
||||
|
||||
// API of tsickle for lowering decorators to properties on the class.
|
||||
if ((<any>typeOrFunc).propDecorators) {
|
||||
if ((<any>typeOrFunc).propDecorators &&
|
||||
(<any>typeOrFunc).propDecorators !== parentCtor.propDecorators) {
|
||||
const propDecorators = (<any>typeOrFunc).propDecorators;
|
||||
const propMetadata = <{[key: string]: any[]}>{};
|
||||
Object.keys(propDecorators).forEach(prop => {
|
||||
@ -125,11 +164,32 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
}
|
||||
|
||||
// API for metadata created by invoking the decorators.
|
||||
if (this._reflect && this._reflect.getMetadata) {
|
||||
const propMetadata = this._reflect.getMetadata('propMetadata', typeOrFunc);
|
||||
if (propMetadata) return propMetadata;
|
||||
if (this._reflect && this._reflect.getOwnMetadata) {
|
||||
return this._reflect.getOwnMetadata('propMetadata', typeOrFunc);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||
const parentCtor = Object.getPrototypeOf(typeOrFunc.prototype).constructor;
|
||||
const propMetadata: {[key: string]: any[]} = {};
|
||||
if (parentCtor !== Object) {
|
||||
const parentPropMetadata = this.propMetadata(parentCtor);
|
||||
Object.keys(parentPropMetadata).forEach((propName) => {
|
||||
propMetadata[propName] = parentPropMetadata[propName];
|
||||
});
|
||||
}
|
||||
const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor);
|
||||
if (ownPropMetadata) {
|
||||
Object.keys(ownPropMetadata).forEach((propName) => {
|
||||
const decorators: any[] = [];
|
||||
if (propMetadata.hasOwnProperty(propName)) {
|
||||
decorators.push(...propMetadata[propName]);
|
||||
}
|
||||
decorators.push(...ownPropMetadata[propName]);
|
||||
propMetadata[propName] = decorators;
|
||||
});
|
||||
}
|
||||
return propMetadata;
|
||||
}
|
||||
|
||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||
|
@ -262,7 +262,7 @@ export function makeDecorator(
|
||||
const metaCtor = makeMetadataCtor([props]);
|
||||
|
||||
function DecoratorFactory(objOrType: any): (cls: any) => any {
|
||||
if (!(Reflect && Reflect.getMetadata)) {
|
||||
if (!(Reflect && Reflect.getOwnMetadata)) {
|
||||
throw 'reflect-metadata shim is required when using class decorators';
|
||||
}
|
||||
|
||||
@ -327,7 +327,7 @@ export function makeParamDecorator(
|
||||
return ParamDecorator;
|
||||
|
||||
function ParamDecorator(cls: any, unusedKey: any, index: number): any {
|
||||
const parameters: any[][] = Reflect.getMetadata('parameters', cls) || [];
|
||||
const parameters: any[][] = Reflect.getOwnMetadata('parameters', cls) || [];
|
||||
|
||||
// there might be gaps if some in between parameters do not have annotations.
|
||||
// we pad with nulls.
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Reflector} from '@angular/core/src/reflection/reflection';
|
||||
import {ReflectionCapabilities} from '@angular/core/src/reflection/reflection_capabilities';
|
||||
import {DELEGATE_CTOR, ReflectionCapabilities} from '@angular/core/src/reflection/reflection_capabilities';
|
||||
import {makeDecorator, makeParamDecorator, makePropDecorator} from '@angular/core/src/util/decorators';
|
||||
|
||||
interface ClassDecoratorFactory {
|
||||
@ -107,7 +107,6 @@ export function main() {
|
||||
class ForwardDep {}
|
||||
expect(reflector.parameters(Forward)).toEqual([[ForwardDep]]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('propMetadata', () => {
|
||||
@ -117,6 +116,15 @@ export function main() {
|
||||
expect(p['c']).toEqual([new PropDecorator('p3')]);
|
||||
expect(p['someMethod']).toEqual([new PropDecorator('p4')]);
|
||||
});
|
||||
|
||||
it('should also return metadata if the class has no decorator', () => {
|
||||
class Test {
|
||||
@PropDecorator('test')
|
||||
prop: any;
|
||||
}
|
||||
|
||||
expect(reflector.propMetadata(Test)).toEqual({'prop': [new PropDecorator('test')]});
|
||||
});
|
||||
});
|
||||
|
||||
describe('annotations', () => {
|
||||
@ -154,5 +162,321 @@ export function main() {
|
||||
expect(func(obj, ['value'])).toEqual('value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ctor inheritance detection', () => {
|
||||
it('should use the right regex', () => {
|
||||
class Parent {}
|
||||
|
||||
class ChildNoCtor extends Parent {}
|
||||
class ChildWithCtor extends Parent {
|
||||
constructor() { super(); }
|
||||
}
|
||||
|
||||
expect(DELEGATE_CTOR.exec(ChildNoCtor.toString())).toBeTruthy();
|
||||
expect(DELEGATE_CTOR.exec(ChildWithCtor.toString())).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('inheritance with decorators', () => {
|
||||
it('should inherit annotations', () => {
|
||||
|
||||
@ClassDecorator({value: 'parent'})
|
||||
class Parent {
|
||||
}
|
||||
|
||||
@ClassDecorator({value: 'child'})
|
||||
class Child extends Parent {
|
||||
}
|
||||
|
||||
class ChildNoDecorators extends Parent {}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.annotations(Parent)).toEqual([new ClassDecorator({value: 'parent'})]);
|
||||
|
||||
expect(reflector.annotations(Child)).toEqual([
|
||||
new ClassDecorator({value: 'parent'}), new ClassDecorator({value: 'child'})
|
||||
]);
|
||||
|
||||
expect(reflector.annotations(ChildNoDecorators)).toEqual([new ClassDecorator(
|
||||
{value: 'parent'})]);
|
||||
});
|
||||
|
||||
it('should inherit parameters', () => {
|
||||
class A {}
|
||||
class B {}
|
||||
class C {}
|
||||
|
||||
// Note: We need the class decorator as well,
|
||||
// as otherwise TS won't capture the ctor arguments!
|
||||
@ClassDecorator({value: 'parent'})
|
||||
class Parent {
|
||||
constructor(@ParamDecorator('a') a: A, @ParamDecorator('b') b: B) {}
|
||||
}
|
||||
|
||||
class Child extends Parent {}
|
||||
|
||||
// Note: We need the class decorator as well,
|
||||
// as otherwise TS won't capture the ctor arguments!
|
||||
@ClassDecorator({value: 'child'})
|
||||
class ChildWithCtor extends Parent {
|
||||
constructor(@ParamDecorator('c') c: C) { super(null, null); }
|
||||
}
|
||||
|
||||
class ChildWithCtorNoDecorator extends Parent {
|
||||
constructor(a: any, b: any, c: any) { super(null, null); }
|
||||
}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.parameters(Parent)).toEqual([
|
||||
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
|
||||
]);
|
||||
|
||||
expect(reflector.parameters(Child)).toEqual([
|
||||
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
|
||||
]);
|
||||
|
||||
expect(reflector.parameters(ChildWithCtor)).toEqual([[C, new ParamDecorator('c')]]);
|
||||
|
||||
// If we have no decorator, we don't get metadata about the ctor params.
|
||||
// But we should still get an array of the right length based on function.length.
|
||||
expect(reflector.parameters(ChildWithCtorNoDecorator)).toEqual([
|
||||
undefined, undefined, undefined
|
||||
]);
|
||||
});
|
||||
|
||||
it('should inherit property metadata', () => {
|
||||
class A {}
|
||||
class B {}
|
||||
class C {}
|
||||
|
||||
class Parent {
|
||||
@PropDecorator('a')
|
||||
a: A;
|
||||
@PropDecorator('b1')
|
||||
b: B;
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
@PropDecorator('b2')
|
||||
b: B;
|
||||
@PropDecorator('c')
|
||||
c: C;
|
||||
}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.propMetadata(Parent)).toEqual({
|
||||
'a': [new PropDecorator('a')],
|
||||
'b': [new PropDecorator('b1')],
|
||||
});
|
||||
|
||||
expect(reflector.propMetadata(Child)).toEqual({
|
||||
'a': [new PropDecorator('a')],
|
||||
'b': [new PropDecorator('b1'), new PropDecorator('b2')],
|
||||
'c': [new PropDecorator('c')]
|
||||
});
|
||||
});
|
||||
|
||||
it('should inherit lifecycle hooks', () => {
|
||||
class Parent {
|
||||
hook1() {}
|
||||
hook2() {}
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
hook2() {}
|
||||
hook3() {}
|
||||
}
|
||||
|
||||
function hooks(symbol: any, names: string[]): boolean[] {
|
||||
return names.map(name => reflector.hasLifecycleHook(symbol, name));
|
||||
}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(hooks(Parent, ['hook1', 'hook2', 'hook3'])).toEqual([true, true, false]);
|
||||
|
||||
expect(hooks(Child, ['hook1', 'hook2', 'hook3'])).toEqual([true, true, true]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('inheritance with tsickle', () => {
|
||||
it('should inherit annotations', () => {
|
||||
|
||||
class Parent {
|
||||
static decorators = [{type: ClassDecorator, args: [{value: 'parent'}]}];
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
static decorators = [{type: ClassDecorator, args: [{value: 'child'}]}];
|
||||
}
|
||||
|
||||
class ChildNoDecorators extends Parent {}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.annotations(Parent)).toEqual([new ClassDecorator({value: 'parent'})]);
|
||||
|
||||
expect(reflector.annotations(Child)).toEqual([
|
||||
new ClassDecorator({value: 'parent'}), new ClassDecorator({value: 'child'})
|
||||
]);
|
||||
|
||||
expect(reflector.annotations(ChildNoDecorators)).toEqual([new ClassDecorator(
|
||||
{value: 'parent'})]);
|
||||
});
|
||||
|
||||
it('should inherit parameters', () => {
|
||||
class A {}
|
||||
class B {}
|
||||
class C {}
|
||||
|
||||
class Parent {
|
||||
static ctorParameters = () =>
|
||||
[{type: A, decorators: [{type: ParamDecorator, args: ['a']}]},
|
||||
{type: B, decorators: [{type: ParamDecorator, args: ['b']}]},
|
||||
];
|
||||
}
|
||||
|
||||
class Child extends Parent {}
|
||||
|
||||
class ChildWithCtor extends Parent {
|
||||
static ctorParameters =
|
||||
() => [{type: C, decorators: [{type: ParamDecorator, args: ['c']}]}, ];
|
||||
constructor() { super(); }
|
||||
}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.parameters(Parent)).toEqual([
|
||||
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
|
||||
]);
|
||||
|
||||
expect(reflector.parameters(Child)).toEqual([
|
||||
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
|
||||
]);
|
||||
|
||||
expect(reflector.parameters(ChildWithCtor)).toEqual([[C, new ParamDecorator('c')]]);
|
||||
});
|
||||
|
||||
it('should inherit property metadata', () => {
|
||||
class A {}
|
||||
class B {}
|
||||
class C {}
|
||||
|
||||
class Parent {
|
||||
static propDecorators: any = {
|
||||
'a': [{type: PropDecorator, args: ['a']}],
|
||||
'b': [{type: PropDecorator, args: ['b1']}],
|
||||
};
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
static propDecorators: any = {
|
||||
'b': [{type: PropDecorator, args: ['b2']}],
|
||||
'c': [{type: PropDecorator, args: ['c']}],
|
||||
};
|
||||
}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.propMetadata(Parent)).toEqual({
|
||||
'a': [new PropDecorator('a')],
|
||||
'b': [new PropDecorator('b1')],
|
||||
});
|
||||
|
||||
expect(reflector.propMetadata(Child)).toEqual({
|
||||
'a': [new PropDecorator('a')],
|
||||
'b': [new PropDecorator('b1'), new PropDecorator('b2')],
|
||||
'c': [new PropDecorator('c')]
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('inheritance with es5 API', () => {
|
||||
it('should inherit annotations', () => {
|
||||
|
||||
class Parent {
|
||||
static annotations = [new ClassDecorator({value: 'parent'})];
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
static annotations = [new ClassDecorator({value: 'child'})];
|
||||
}
|
||||
|
||||
class ChildNoDecorators extends Parent {}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.annotations(Parent)).toEqual([new ClassDecorator({value: 'parent'})]);
|
||||
|
||||
expect(reflector.annotations(Child)).toEqual([
|
||||
new ClassDecorator({value: 'parent'}), new ClassDecorator({value: 'child'})
|
||||
]);
|
||||
|
||||
expect(reflector.annotations(ChildNoDecorators)).toEqual([new ClassDecorator(
|
||||
{value: 'parent'})]);
|
||||
});
|
||||
|
||||
it('should inherit parameters', () => {
|
||||
class A {}
|
||||
class B {}
|
||||
class C {}
|
||||
|
||||
class Parent {
|
||||
static parameters = [
|
||||
[A, new ParamDecorator('a')],
|
||||
[B, new ParamDecorator('b')],
|
||||
];
|
||||
}
|
||||
|
||||
class Child extends Parent {}
|
||||
|
||||
class ChildWithCtor extends Parent {
|
||||
static parameters = [
|
||||
[C, new ParamDecorator('c')],
|
||||
];
|
||||
constructor() { super(); }
|
||||
}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.parameters(Parent)).toEqual([
|
||||
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
|
||||
]);
|
||||
|
||||
expect(reflector.parameters(Child)).toEqual([
|
||||
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
|
||||
]);
|
||||
|
||||
expect(reflector.parameters(ChildWithCtor)).toEqual([[C, new ParamDecorator('c')]]);
|
||||
});
|
||||
|
||||
it('should inherit property metadata', () => {
|
||||
class A {}
|
||||
class B {}
|
||||
class C {}
|
||||
|
||||
class Parent {
|
||||
static propMetadata: any = {
|
||||
'a': [new PropDecorator('a')],
|
||||
'b': [new PropDecorator('b1')],
|
||||
};
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
static propMetadata: any = {
|
||||
'b': [new PropDecorator('b2')],
|
||||
'c': [new PropDecorator('c')],
|
||||
};
|
||||
}
|
||||
|
||||
// Check that metadata for Parent was not changed!
|
||||
expect(reflector.propMetadata(Parent)).toEqual({
|
||||
'a': [new PropDecorator('a')],
|
||||
'b': [new PropDecorator('b1')],
|
||||
});
|
||||
|
||||
expect(reflector.propMetadata(Child)).toEqual({
|
||||
'a': [new PropDecorator('a')],
|
||||
'b': [new PropDecorator('b1'), new PropDecorator('b2')],
|
||||
'c': [new PropDecorator('c')]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ export function main() {
|
||||
it('should invoke as decorator', () => {
|
||||
function Type() {}
|
||||
TestDecorator({marker: 'WORKS'})(Type);
|
||||
const annotations = Reflect.getMetadata('annotations', Type);
|
||||
const annotations = Reflect.getOwnMetadata('annotations', Type);
|
||||
expect(annotations[0].marker).toEqual('WORKS');
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user