From 1809250c258afb8b873b6f63df93c4c674604357 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 29 Jun 2020 19:06:25 -0700 Subject: [PATCH] Emit fallback for decorator metadata for type only imports --- src/compiler/checker.ts | 16 ++--- .../decoratorMetadataWithTypeOnlyImport.js | 60 +++++++++++++++++++ ...ecoratorMetadataWithTypeOnlyImport.symbols | 30 ++++++++++ .../decoratorMetadataWithTypeOnlyImport.types | 29 +++++++++ .../decoratorMetadataWithTypeOnlyImport.ts | 21 +++++++ 5 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.js create mode 100644 tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.symbols create mode 100644 tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.types create mode 100644 tests/cases/conformance/decorators/decoratorMetadataWithTypeOnlyImport.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ad58313c4cc8..18a3457caf33e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36973,29 +36973,31 @@ namespace ts { } // Resolve the symbol as a value to ensure the type can be reached at runtime during emit. - const valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location); + const valueSymbol = resolveEntityName(typeName, SymbolFlags.Value, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, location); + const isTypeOnly = valueSymbol?.declarations?.every(isTypeOnlyImportOrExportDeclaration) || false; + const resolvedSymbol = valueSymbol && valueSymbol.flags & SymbolFlags.Alias ? resolveAlias(valueSymbol) : valueSymbol; // Resolve the symbol as a type so that we can provide a more useful hint for the type serializer. const typeSymbol = resolveEntityName(typeName, SymbolFlags.Type, /*ignoreErrors*/ true, /*dontResolveAlias*/ false, location); - if (valueSymbol && valueSymbol === typeSymbol) { + if (resolvedSymbol && resolvedSymbol === typeSymbol) { const globalPromiseSymbol = getGlobalPromiseConstructorSymbol(/*reportErrors*/ false); - if (globalPromiseSymbol && valueSymbol === globalPromiseSymbol) { + if (globalPromiseSymbol && resolvedSymbol === globalPromiseSymbol) { return TypeReferenceSerializationKind.Promise; } - const constructorType = getTypeOfSymbol(valueSymbol); + const constructorType = getTypeOfSymbol(resolvedSymbol); if (constructorType && isConstructorType(constructorType)) { - return TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue; + return isTypeOnly ? TypeReferenceSerializationKind.TypeWithCallSignature : TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue; } } // We might not be able to resolve type symbol so use unknown type in that case (eg error case) if (!typeSymbol) { - return TypeReferenceSerializationKind.Unknown; + return isTypeOnly ? TypeReferenceSerializationKind.ObjectType : TypeReferenceSerializationKind.Unknown; } const type = getDeclaredTypeOfSymbol(typeSymbol); if (type === errorType) { - return TypeReferenceSerializationKind.Unknown; + return isTypeOnly ? TypeReferenceSerializationKind.ObjectType : TypeReferenceSerializationKind.Unknown; } else if (type.flags & TypeFlags.AnyOrUnknown) { return TypeReferenceSerializationKind.ObjectType; diff --git a/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.js b/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.js new file mode 100644 index 0000000000000..0596ecfa12be6 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.js @@ -0,0 +1,60 @@ +//// [tests/cases/conformance/decorators/decoratorMetadataWithTypeOnlyImport.ts] //// + +//// [service.ts] +export class Service { +} +//// [component.ts] +import type { Service } from "./service"; + +declare var decorator: any; + +@decorator +class MyComponent { + constructor(public Service: Service) { + } + + @decorator + method(x: this) { + } +} + +//// [service.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Service = void 0; +var Service = /** @class */ (function () { + function Service() { + } + return Service; +}()); +exports.Service = Service; +//// [component.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); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var MyComponent = /** @class */ (function () { + function MyComponent(Service) { + this.Service = Service; + } + MyComponent.prototype.method = function (x) { + }; + __decorate([ + decorator, + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], MyComponent.prototype, "method", null); + MyComponent = __decorate([ + decorator, + __metadata("design:paramtypes", [Function]) + ], MyComponent); + return MyComponent; +}()); diff --git a/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.symbols b/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.symbols new file mode 100644 index 0000000000000..a36cba8c590e1 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.symbols @@ -0,0 +1,30 @@ +=== tests/cases/conformance/decorators/service.ts === +export class Service { +>Service : Symbol(Service, Decl(service.ts, 0, 0)) +} +=== tests/cases/conformance/decorators/component.ts === +import type { Service } from "./service"; +>Service : Symbol(Service, Decl(component.ts, 0, 13)) + +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(MyComponent.Service, Decl(component.ts, 6, 16)) +>Service : Symbol(Service, Decl(component.ts, 0, 13)) + } + + @decorator +>decorator : Symbol(decorator, Decl(component.ts, 2, 11)) + + method(x: this) { +>method : Symbol(MyComponent.method, Decl(component.ts, 7, 5)) +>x : Symbol(x, Decl(component.ts, 10, 11)) + } +} diff --git a/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.types b/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.types new file mode 100644 index 0000000000000..125380a21a9a0 --- /dev/null +++ b/tests/baselines/reference/decoratorMetadataWithTypeOnlyImport.types @@ -0,0 +1,29 @@ +=== tests/cases/conformance/decorators/service.ts === +export class Service { +>Service : Service +} +=== tests/cases/conformance/decorators/component.ts === +import type { Service } from "./service"; +>Service : Service + +declare var decorator: any; +>decorator : any + +@decorator +>decorator : any + +class MyComponent { +>MyComponent : MyComponent + + constructor(public Service: Service) { +>Service : Service + } + + @decorator +>decorator : any + + method(x: this) { +>method : (x: this) => void +>x : this + } +} diff --git a/tests/cases/conformance/decorators/decoratorMetadataWithTypeOnlyImport.ts b/tests/cases/conformance/decorators/decoratorMetadataWithTypeOnlyImport.ts new file mode 100644 index 0000000000000..e0f26d0518c44 --- /dev/null +++ b/tests/cases/conformance/decorators/decoratorMetadataWithTypeOnlyImport.ts @@ -0,0 +1,21 @@ +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +// @target: es5 +// @module: commonjs +// @filename: service.ts +export class Service { +} +// @filename: component.ts +import type { Service } from "./service"; + +declare var decorator: any; + +@decorator +class MyComponent { + constructor(public Service: Service) { + } + + @decorator + method(x: this) { + } +} \ No newline at end of file