diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f8bafa7a38f..2fb68eb7ab8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14016,15 +14016,17 @@ namespace ts { return type.flags & TypeFlags.ObjectType && getSignaturesOfType(type, SignatureKind.Call).length > 0; } - function getTypeReferenceSerializationKind(node: TypeReferenceNode): TypeReferenceSerializationKind { + function getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind { // Resolve the symbol as a value to ensure the type can be reached at runtime during emit. - let symbol = resolveEntityName(node.typeName, SymbolFlags.Value, /*ignoreErrors*/ true); - let constructorType = symbol ? getTypeOfSymbol(symbol) : undefined; + let valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true); + let constructorType = valueSymbol ? getTypeOfSymbol(valueSymbol) : undefined; if (constructorType && isConstructorType(constructorType)) { return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue; } - let type = getTypeFromTypeNode(node); + // Resolve the symbol as a type so that we can provide a more useful hint for the type serializer. + let typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true); + let type = getDeclaredTypeOfSymbol(typeSymbol); if (type === unknownType) { return TypeReferenceSerializationKind.Unknown; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9c8438565e7..98de37c752c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4897,8 +4897,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */ function emitSerializedTypeReferenceNode(node: TypeReferenceNode) { - let typeName = node.typeName; - let result = resolver.getTypeReferenceSerializationKind(node); + let location: Node = node.parent; + while (isDeclaration(location) || isTypeNode(location)) { + location = location.parent; + } + + // Clone the type name and parent it to a location outside of the current declaration. + let typeName = cloneEntityName(node.typeName); + typeName.parent = location; + + let result = resolver.getTypeReferenceSerializationKind(typeName); switch (result) { case TypeReferenceSerializationKind.Unknown: let temp = createAndRecordTempVariable(TempFlags.Auto); @@ -5030,6 +5038,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi argumentsWritten++; } if (shouldEmitParamTypesMetadata(node)) { + debugger; if (writeComma || argumentsWritten) { write(", "); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e0f16a00c2b..bba3d81da52 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1573,7 +1573,7 @@ namespace ts { getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; getBlockScopedVariableId(node: Identifier): number; getReferencedValueDeclaration(reference: Identifier): Declaration; - getTypeReferenceSerializationKind(node: TypeReferenceNode): TypeReferenceSerializationKind; + getTypeReferenceSerializationKind(typeName: EntityName): TypeReferenceSerializationKind; } export const enum SymbolFlags { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5174589cdcf..79c16716be5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1424,6 +1424,22 @@ namespace ts { return isFunctionLike(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile; } + export function cloneEntityName(node: EntityName): EntityName { + if (node.kind === SyntaxKind.Identifier) { + let clone = createSynthesizedNode(SyntaxKind.Identifier); + clone.text = (node).text; + return clone; + } + else { + let clone = createSynthesizedNode(SyntaxKind.QualifiedName); + clone.left = cloneEntityName((node).left); + clone.left.parent = clone; + clone.right = cloneEntityName((node).right); + clone.right.parent = clone; + return clone; + } + } + export function nodeIsSynthesized(node: Node): boolean { return node.pos === -1; } diff --git a/tests/baselines/reference/decoratorMetadata.js b/tests/baselines/reference/decoratorMetadata.js new file mode 100644 index 00000000000..71dc4de383a --- /dev/null +++ b/tests/baselines/reference/decoratorMetadata.js @@ -0,0 +1,45 @@ +//// [tests/cases/conformance/decorators/decoratorMetadata.ts] //// + +//// [service.ts] +export default class Service { +} +//// [component.ts] +import Service from "./service"; + +declare var decorator: any; + +@decorator +class MyComponent { + constructor(public Service: Service) { + } +} + +//// [service.js] +var Service = (function () { + function Service() { + } + return Service; +})(); +exports.default = Service; +//// [component.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); + switch (arguments.length) { + case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); + case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); + case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); + } +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var MyComponent = (function () { + function MyComponent(Service) { + this.Service = Service; + } + MyComponent = __decorate([ + decorator, + __metadata('design:paramtypes', [service_1.default]) + ], MyComponent); + return MyComponent; +})(); diff --git a/tests/baselines/reference/decoratorMetadata.symbols b/tests/baselines/reference/decoratorMetadata.symbols new file mode 100644 index 00000000000..706f5ace8a9 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadata.symbols @@ -0,0 +1,22 @@ +=== tests/cases/conformance/decorators/service.ts === +export default class Service { +>Service : Symbol(Service, Decl(service.ts, 0, 0)) +} +=== tests/cases/conformance/decorators/component.ts === +import Service from "./service"; +>Service : Symbol(Service, Decl(component.ts, 0, 6)) + +declare var decorator: any; +>decorator : Symbol(decorator, Decl(component.ts, 2, 11)) + +@decorator +>decorator : Symbol(decorator, Decl(component.ts, 2, 11)) + +class MyComponent { +>MyComponent : Symbol(MyComponent, Decl(component.ts, 2, 27)) + + constructor(public Service: Service) { +>Service : Symbol(Service, Decl(component.ts, 6, 16)) +>Service : Symbol(Service, Decl(component.ts, 0, 6)) + } +} diff --git a/tests/baselines/reference/decoratorMetadata.types b/tests/baselines/reference/decoratorMetadata.types new file mode 100644 index 00000000000..b0ce80fb1e4 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadata.types @@ -0,0 +1,22 @@ +=== tests/cases/conformance/decorators/service.ts === +export default class Service { +>Service : Service +} +=== tests/cases/conformance/decorators/component.ts === +import Service from "./service"; +>Service : typeof Service + +declare var decorator: any; +>decorator : any + +@decorator +>decorator : any + +class MyComponent { +>MyComponent : MyComponent + + constructor(public Service: Service) { +>Service : Service +>Service : Service + } +} diff --git a/tests/cases/conformance/decorators/decoratorMetadata.ts b/tests/cases/conformance/decorators/decoratorMetadata.ts new file mode 100644 index 00000000000..3f622909309 --- /dev/null +++ b/tests/cases/conformance/decorators/decoratorMetadata.ts @@ -0,0 +1,17 @@ +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +// @target: es5 +// @module: commonjs +// @filename: service.ts +export default class Service { +} +// @filename: component.ts +import Service from "./service"; + +declare var decorator: any; + +@decorator +class MyComponent { + constructor(public Service: Service) { + } +} \ No newline at end of file