diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 686efaa758d34..e6d21588db352 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16581,6 +16581,10 @@ namespace ts { } } + function getParameterTypeNodeForDecoratorCheck(node: ParameterDeclaration): TypeNode { + return node.dotDotDotToken ? getRestParameterElementType(node.type) : node.type; + } + /** Check the decorators of a node */ function checkDecorators(node: Node): void { if (!node.decorators) { @@ -16612,7 +16616,7 @@ namespace ts { const constructor = getFirstConstructorWithBody(node); if (constructor) { for (const parameter of constructor.parameters) { - markTypeNodeAsReferenced(parameter.type); + markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); } } break; @@ -16621,15 +16625,17 @@ namespace ts { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: for (const parameter of (node).parameters) { - markTypeNodeAsReferenced(parameter.type); + markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(parameter)); } markTypeNodeAsReferenced((node).type); break; case SyntaxKind.PropertyDeclaration: + markTypeNodeAsReferenced(getParameterTypeNodeForDecoratorCheck(node)); + break; case SyntaxKind.Parameter: - markTypeNodeAsReferenced((node).type); + markTypeNodeAsReferenced((node).type); break; } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index a357781ca30bd..877131b369c87 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1577,24 +1577,6 @@ namespace ts { } } - /** - * Gets the most likely element type for a TypeNode. This is not an exhaustive test - * as it assumes a rest argument can only be an array type (either T[], or Array). - * - * @param node The type node. - */ - function getRestParameterElementType(node: TypeNode) { - if (node && node.kind === SyntaxKind.ArrayType) { - return (node).elementType; - } - else if (node && node.kind === SyntaxKind.TypeReference) { - return singleOrUndefined((node).typeArguments); - } - else { - return undefined; - } - } - /** * Serializes the types of the parameters of a node for use with decorator type metadata. * diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3dd7054a40530..6f491f6708fee 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -803,6 +803,23 @@ namespace ts { } } + /** + * Gets the most likely element type for a TypeNode. This is not an exhaustive test + * as it assumes a rest argument can only be an array type (either T[], or Array). + * + * @param node The type node. + */ + export function getRestParameterElementType(node: TypeNode) { + if (node && node.kind === SyntaxKind.ArrayType) { + return (node).elementType; + } + else if (node && node.kind === SyntaxKind.TypeReference) { + return singleOrUndefined((node).typeArguments); + } + else { + return undefined; + } + } export function isVariableLike(node: Node): node is VariableLikeDeclaration { if (node) { diff --git a/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.js b/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.js new file mode 100644 index 0000000000000..be249800d578a --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.js @@ -0,0 +1,112 @@ +//// [tests/cases/compiler/decoratorMetadataRestParameterWithImportedType.ts] //// + +//// [aux.ts] + +export class SomeClass { + field: string; +} + +//// [aux1.ts] +export class SomeClass1 { + field: string; +} + +//// [aux2.ts] +export class SomeClass2 { + field: string; +} +//// [main.ts] +import { SomeClass } from './aux'; +import { SomeClass1 } from './aux1'; + +function annotation(): ClassDecorator { + return (target: any): void => { }; +} + +function annotation1(): MethodDecorator { + return (target: any): void => { }; +} + +@annotation() +export class ClassA { + array: SomeClass[]; + + constructor(...init: SomeClass[]) { + this.array = init; + } + + @annotation1() + foo(... args: SomeClass1[]) { + } +} + +//// [aux.js] +"use strict"; +var SomeClass = (function () { + function SomeClass() { + } + return SomeClass; +}()); +exports.SomeClass = SomeClass; +//// [aux1.js] +"use strict"; +var SomeClass1 = (function () { + function SomeClass1() { + } + return SomeClass1; +}()); +exports.SomeClass1 = SomeClass1; +//// [aux2.js] +"use strict"; +var SomeClass2 = (function () { + function SomeClass2() { + } + return SomeClass2; +}()); +exports.SomeClass2 = SomeClass2; +//// [main.js] +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var aux_1 = require("./aux"); +var aux1_1 = require("./aux1"); +function annotation() { + return function (target) { }; +} +function annotation1() { + return function (target) { }; +} +var ClassA = (function () { + function ClassA() { + var init = []; + for (var _i = 0; _i < arguments.length; _i++) { + init[_i] = arguments[_i]; + } + this.array = init; + } + ClassA.prototype.foo = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + }; + return ClassA; +}()); +__decorate([ + annotation1(), + __metadata("design:type", Function), + __metadata("design:paramtypes", [aux1_1.SomeClass1]), + __metadata("design:returntype", void 0) +], ClassA.prototype, "foo", null); +ClassA = __decorate([ + annotation(), + __metadata("design:paramtypes", [aux_1.SomeClass]) +], ClassA); +exports.ClassA = ClassA; diff --git a/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.symbols b/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.symbols new file mode 100644 index 0000000000000..9bd293cdfcb10 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.symbols @@ -0,0 +1,77 @@ +=== tests/cases/compiler/aux.ts === + +export class SomeClass { +>SomeClass : Symbol(SomeClass, Decl(aux.ts, 0, 0)) + + field: string; +>field : Symbol(SomeClass.field, Decl(aux.ts, 1, 24)) +} + +=== tests/cases/compiler/aux1.ts === +export class SomeClass1 { +>SomeClass1 : Symbol(SomeClass1, Decl(aux1.ts, 0, 0)) + + field: string; +>field : Symbol(SomeClass1.field, Decl(aux1.ts, 0, 25)) +} + +=== tests/cases/compiler/aux2.ts === +export class SomeClass2 { +>SomeClass2 : Symbol(SomeClass2, Decl(aux2.ts, 0, 0)) + + field: string; +>field : Symbol(SomeClass2.field, Decl(aux2.ts, 0, 25)) +} +=== tests/cases/compiler/main.ts === +import { SomeClass } from './aux'; +>SomeClass : Symbol(SomeClass, Decl(main.ts, 0, 8)) + +import { SomeClass1 } from './aux1'; +>SomeClass1 : Symbol(SomeClass1, Decl(main.ts, 1, 8)) + +function annotation(): ClassDecorator { +>annotation : Symbol(annotation, Decl(main.ts, 1, 36)) +>ClassDecorator : Symbol(ClassDecorator, Decl(lib.d.ts, --, --)) + + return (target: any): void => { }; +>target : Symbol(target, Decl(main.ts, 4, 12)) +} + +function annotation1(): MethodDecorator { +>annotation1 : Symbol(annotation1, Decl(main.ts, 5, 1)) +>MethodDecorator : Symbol(MethodDecorator, Decl(lib.d.ts, --, --)) + + return (target: any): void => { }; +>target : Symbol(target, Decl(main.ts, 8, 12)) +} + +@annotation() +>annotation : Symbol(annotation, Decl(main.ts, 1, 36)) + +export class ClassA { +>ClassA : Symbol(ClassA, Decl(main.ts, 9, 1)) + + array: SomeClass[]; +>array : Symbol(ClassA.array, Decl(main.ts, 12, 21)) +>SomeClass : Symbol(SomeClass, Decl(main.ts, 0, 8)) + + constructor(...init: SomeClass[]) { +>init : Symbol(init, Decl(main.ts, 15, 16)) +>SomeClass : Symbol(SomeClass, Decl(main.ts, 0, 8)) + + this.array = init; +>this.array : Symbol(ClassA.array, Decl(main.ts, 12, 21)) +>this : Symbol(ClassA, Decl(main.ts, 9, 1)) +>array : Symbol(ClassA.array, Decl(main.ts, 12, 21)) +>init : Symbol(init, Decl(main.ts, 15, 16)) + } + + @annotation1() +>annotation1 : Symbol(annotation1, Decl(main.ts, 5, 1)) + + foo(... args: SomeClass1[]) { +>foo : Symbol(ClassA.foo, Decl(main.ts, 17, 5)) +>args : Symbol(args, Decl(main.ts, 20, 8)) +>SomeClass1 : Symbol(SomeClass1, Decl(main.ts, 1, 8)) + } +} diff --git a/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.types b/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.types new file mode 100644 index 0000000000000..6c27aa2c94210 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataRestParameterWithImportedType.types @@ -0,0 +1,82 @@ +=== tests/cases/compiler/aux.ts === + +export class SomeClass { +>SomeClass : SomeClass + + field: string; +>field : string +} + +=== tests/cases/compiler/aux1.ts === +export class SomeClass1 { +>SomeClass1 : SomeClass1 + + field: string; +>field : string +} + +=== tests/cases/compiler/aux2.ts === +export class SomeClass2 { +>SomeClass2 : SomeClass2 + + field: string; +>field : string +} +=== tests/cases/compiler/main.ts === +import { SomeClass } from './aux'; +>SomeClass : typeof SomeClass + +import { SomeClass1 } from './aux1'; +>SomeClass1 : typeof SomeClass1 + +function annotation(): ClassDecorator { +>annotation : () => ClassDecorator +>ClassDecorator : ClassDecorator + + return (target: any): void => { }; +>(target: any): void => { } : (target: any) => void +>target : any +} + +function annotation1(): MethodDecorator { +>annotation1 : () => MethodDecorator +>MethodDecorator : MethodDecorator + + return (target: any): void => { }; +>(target: any): void => { } : (target: any) => void +>target : any +} + +@annotation() +>annotation() : ClassDecorator +>annotation : () => ClassDecorator + +export class ClassA { +>ClassA : ClassA + + array: SomeClass[]; +>array : SomeClass[] +>SomeClass : SomeClass + + constructor(...init: SomeClass[]) { +>init : SomeClass[] +>SomeClass : SomeClass + + this.array = init; +>this.array = init : SomeClass[] +>this.array : SomeClass[] +>this : this +>array : SomeClass[] +>init : SomeClass[] + } + + @annotation1() +>annotation1() : MethodDecorator +>annotation1 : () => MethodDecorator + + foo(... args: SomeClass1[]) { +>foo : (...args: SomeClass1[]) => void +>args : SomeClass1[] +>SomeClass1 : SomeClass1 + } +} diff --git a/tests/cases/compiler/decoratorMetadataRestParameterWithImportedType.ts b/tests/cases/compiler/decoratorMetadataRestParameterWithImportedType.ts new file mode 100644 index 0000000000000..3bc22df89284c --- /dev/null +++ b/tests/cases/compiler/decoratorMetadataRestParameterWithImportedType.ts @@ -0,0 +1,42 @@ +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +// @target: es5 + +// @filename: aux.ts +export class SomeClass { + field: string; +} + +// @filename: aux1.ts +export class SomeClass1 { + field: string; +} + +// @filename: aux2.ts +export class SomeClass2 { + field: string; +} +// @filename: main.ts +import { SomeClass } from './aux'; +import { SomeClass1 } from './aux1'; + +function annotation(): ClassDecorator { + return (target: any): void => { }; +} + +function annotation1(): MethodDecorator { + return (target: any): void => { }; +} + +@annotation() +export class ClassA { + array: SomeClass[]; + + constructor(...init: SomeClass[]) { + this.array = init; + } + + @annotation1() + foo(... args: SomeClass1[]) { + } +} \ No newline at end of file