From 724550de0de80a9716996a5786a8dcf5f066d05c Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 22 Feb 2021 11:18:03 -0800 Subject: [PATCH 1/6] Add isolatedModules error for ambiguous imports referenced in decorator metadata --- src/compiler/checker.ts | 28 ++++++++++++------ src/compiler/diagnosticMessages.json | 4 +++ .../emitDecoratorMetadata_isolatedModules.ts | 29 +++++++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7c7fab735856e..38e989d67e424 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33447,21 +33447,31 @@ namespace ts { * marked as referenced to prevent import elision. */ function markTypeNodeAsReferenced(node: TypeNode) { - markEntityNameOrEntityExpressionAsReference(node && getEntityNameFromTypeNode(node)); + markEntityNameOrEntityExpressionAsReference(node && getEntityNameFromTypeNode(node), /*forDecoratorMetadata*/ false); } - function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression | undefined) { + function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression | undefined, forDecoratorMetadata: boolean) { if (!typeName) return; const rootName = getFirstIdentifier(typeName); const meaning = (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias; const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isRefernce*/ true); - if (rootSymbol - && rootSymbol.flags & SymbolFlags.Alias - && symbolIsValue(rootSymbol) - && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol)) - && !getTypeOnlyAliasDeclaration(rootSymbol)) { - markAliasSymbolAsReferenced(rootSymbol); + if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias) { + if (symbolIsValue(rootSymbol) + && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol)) + && !getTypeOnlyAliasDeclaration(rootSymbol)) { + markAliasSymbolAsReferenced(rootSymbol); + } + else if (forDecoratorMetadata + && compilerOptions.isolatedModules + && !symbolIsValue(rootSymbol) + && !some(rootSymbol.declarations, isTypeOnlyImportOrExportDeclaration)) { + const diag = error(typeName, Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_when_isolatedModules_and_emitDecoratorMetadata_are_enabled); + const aliasDeclaration = find(rootSymbol.declarations, isAliasSymbolDeclaration); + if (aliasDeclaration) { + addRelatedInfo(diag, createDiagnosticForNode(aliasDeclaration, Diagnostics._0_was_imported_here, idText(rootName))); + } + } } } @@ -33475,7 +33485,7 @@ namespace ts { function markDecoratorMedataDataTypeNodeAsReferenced(node: TypeNode | undefined): void { const entityName = getEntityNameForDecoratorMetadata(node); if (entityName && isEntityName(entityName)) { - markEntityNameOrEntityExpressionAsReference(entityName); + markEntityNameOrEntityExpressionAsReference(entityName, /*forDecoratorMetadata*/ true); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index bda4c72737364..b28b096c566ee 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -879,6 +879,10 @@ "category": "Error", "code": 1266 }, + "A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled.": { + "category": "Error", + "code": 1267 + }, "'with' statements are not allowed in an async function block.": { "category": "Error", diff --git a/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts new file mode 100644 index 0000000000000..5c53e2b140ad8 --- /dev/null +++ b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts @@ -0,0 +1,29 @@ +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +// @isolatedModules: true + +// @Filename: type1.ts +interface T1 {} +export type { T1 } + +// @Filename: type2.ts +export interface T2 {} + +// @Filename: index.ts +import { T1 } from "./type1"; +import type { T2 } from "./type2"; +declare var EventListener: any; + +class HelloWorld { + @EventListener('1') + handleEvent1(event: T1) {} // Error + + @EventListener('2') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + + @EventListener('2') + p2!: T2; // Ok +} From 4da4da17239d0751ef8b46629b008f4482f5178b Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 22 Feb 2021 11:23:36 -0800 Subject: [PATCH 2/6] Improve test and accept baselines --- ...coratorMetadata_isolatedModules.errors.txt | 47 +++++++++ .../emitDecoratorMetadata_isolatedModules.js | 99 +++++++++++++++++++ ...tDecoratorMetadata_isolatedModules.symbols | 72 ++++++++++++++ ...mitDecoratorMetadata_isolatedModules.types | 74 ++++++++++++++ .../emitDecoratorMetadata_isolatedModules.ts | 7 ++ 5 files changed, 299 insertions(+) create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules.errors.txt create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules.js create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules.symbols create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules.types diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.errors.txt b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.errors.txt new file mode 100644 index 0000000000000..cabda9caa7e94 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.errors.txt @@ -0,0 +1,47 @@ +tests/cases/compiler/index.ts(8,23): error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(14,8): error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(20,28): error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. + + +==== tests/cases/compiler/type1.ts (0 errors) ==== + interface T1 {} + export type { T1 } + +==== tests/cases/compiler/type2.ts (0 errors) ==== + export interface T2 {} + +==== tests/cases/compiler/class3.ts (0 errors) ==== + export class C3 {} + +==== tests/cases/compiler/index.ts (3 errors) ==== + import { T1 } from "./type1"; + import type { T2 } from "./type2"; + import { C3 } from "./class3"; + declare var EventListener: any; + + class HelloWorld { + @EventListener('1') + handleEvent1(event: T1) {} // Error + ~~ +!!! error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. + + @EventListener('2') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + ~~ +!!! error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. + + @EventListener('2') + p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error + ~~ +!!! error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. + } + \ No newline at end of file diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.js b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.js new file mode 100644 index 0000000000000..bb5e71383f494 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.js @@ -0,0 +1,99 @@ +//// [tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts] //// + +//// [type1.ts] +interface T1 {} +export type { T1 } + +//// [type2.ts] +export interface T2 {} + +//// [class3.ts] +export class C3 {} + +//// [index.ts] +import { T1 } from "./type1"; +import type { T2 } from "./type2"; +import { C3 } from "./class3"; +declare var EventListener: any; + +class HelloWorld { + @EventListener('1') + handleEvent1(event: T1) {} // Error + + @EventListener('2') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + + @EventListener('2') + p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +} + + +//// [type1.js] +"use strict"; +exports.__esModule = true; +//// [type2.js] +"use strict"; +exports.__esModule = true; +//// [class3.js] +"use strict"; +exports.__esModule = true; +exports.C3 = void 0; +var C3 = /** @class */ (function () { + function C3() { + } + return C3; +}()); +exports.C3 = C3; +//// [index.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); +}; +exports.__esModule = true; +var class3_1 = require("./class3"); +var HelloWorld = /** @class */ (function () { + function HelloWorld() { + } + HelloWorld.prototype.handleEvent1 = function (event) { }; // Error + HelloWorld.prototype.handleEvent2 = function (event) { }; // Ok + HelloWorld.prototype.handleEvent3 = function (event) { return undefined; }; // Ok, Error + __decorate([ + EventListener('1'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], HelloWorld.prototype, "handleEvent1"); + __decorate([ + EventListener('2'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], HelloWorld.prototype, "handleEvent2"); + __decorate([ + EventListener('1'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p1"); + __decorate([ + EventListener('2'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p2"); + __decorate([ + EventListener('3'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [class3_1.C3]), + __metadata("design:returntype", Object) + ], HelloWorld.prototype, "handleEvent3"); + return HelloWorld; +}()); diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.symbols b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.symbols new file mode 100644 index 0000000000000..c957b1781e3ea --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.symbols @@ -0,0 +1,72 @@ +=== tests/cases/compiler/type1.ts === +interface T1 {} +>T1 : Symbol(T1, Decl(type1.ts, 0, 0)) + +export type { T1 } +>T1 : Symbol(T1, Decl(type1.ts, 1, 13)) + +=== tests/cases/compiler/type2.ts === +export interface T2 {} +>T2 : Symbol(T2, Decl(type2.ts, 0, 0)) + +=== tests/cases/compiler/class3.ts === +export class C3 {} +>C3 : Symbol(C3, Decl(class3.ts, 0, 0)) + +=== tests/cases/compiler/index.ts === +import { T1 } from "./type1"; +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + +import type { T2 } from "./type2"; +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + +import { C3 } from "./class3"; +>C3 : Symbol(C3, Decl(index.ts, 2, 8)) + +declare var EventListener: any; +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + +class HelloWorld { +>HelloWorld : Symbol(HelloWorld, Decl(index.ts, 3, 31)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent1(event: T1) {} // Error +>handleEvent1 : Symbol(HelloWorld.handleEvent1, Decl(index.ts, 5, 18)) +>event : Symbol(event, Decl(index.ts, 7, 15)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + + @EventListener('2') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent2(event: T2) {} // Ok +>handleEvent2 : Symbol(HelloWorld.handleEvent2, Decl(index.ts, 7, 28)) +>event : Symbol(event, Decl(index.ts, 10, 15)) +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + p1!: T1; // Error +>p1 : Symbol(HelloWorld.p1, Decl(index.ts, 10, 28)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + + @EventListener('2') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + p2!: T2; // Ok +>p2 : Symbol(HelloWorld.p2, Decl(index.ts, 13, 10)) +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + + @EventListener('3') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>handleEvent3 : Symbol(HelloWorld.handleEvent3, Decl(index.ts, 16, 10)) +>event : Symbol(event, Decl(index.ts, 19, 15)) +>C3 : Symbol(C3, Decl(index.ts, 2, 8)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) +>undefined : Symbol(undefined) +} + diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.types b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.types new file mode 100644 index 0000000000000..0048771bb4eac --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules.types @@ -0,0 +1,74 @@ +=== tests/cases/compiler/type1.ts === +interface T1 {} +export type { T1 } +>T1 : T1 + +=== tests/cases/compiler/type2.ts === +export interface T2 {} +No type information for this code. +No type information for this code.=== tests/cases/compiler/class3.ts === +export class C3 {} +>C3 : C3 + +=== tests/cases/compiler/index.ts === +import { T1 } from "./type1"; +>T1 : any + +import type { T2 } from "./type2"; +>T2 : T2 + +import { C3 } from "./class3"; +>C3 : typeof C3 + +declare var EventListener: any; +>EventListener : any + +class HelloWorld { +>HelloWorld : HelloWorld + + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + handleEvent1(event: T1) {} // Error +>handleEvent1 : (event: T1) => void +>event : T1 + + @EventListener('2') +>EventListener('2') : any +>EventListener : any +>'2' : "2" + + handleEvent2(event: T2) {} // Ok +>handleEvent2 : (event: T2) => void +>event : T2 + + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + p1!: T1; // Error +>p1 : T1 + + @EventListener('2') +>EventListener('2') : any +>EventListener : any +>'2' : "2" + + p2!: T2; // Ok +>p2 : T2 + + @EventListener('3') +>EventListener('3') : any +>EventListener : any +>'3' : "3" + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>handleEvent3 : (event: C3) => T1 +>event : C3 +>undefined! : undefined +>undefined : undefined +} + diff --git a/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts index 5c53e2b140ad8..324d6218abda5 100644 --- a/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts +++ b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts @@ -9,9 +9,13 @@ export type { T1 } // @Filename: type2.ts export interface T2 {} +// @Filename: class3.ts +export class C3 {} + // @Filename: index.ts import { T1 } from "./type1"; import type { T2 } from "./type2"; +import { C3 } from "./class3"; declare var EventListener: any; class HelloWorld { @@ -26,4 +30,7 @@ class HelloWorld { @EventListener('2') p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error } From 33664a2295b203db99bba61cb349a994d7a1bc09 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 3 May 2021 10:30:30 -0700 Subject: [PATCH 3/6] Error only for es2015+ --- src/compiler/checker.ts | 1 + ...tadata_isolatedModules(module=commonjs).js | 99 +++++++++++++++++++ ...a_isolatedModules(module=commonjs).symbols | 72 ++++++++++++++ ...ata_isolatedModules(module=commonjs).types | 74 ++++++++++++++ ..._isolatedModules(module=esnext).errors.txt | 47 +++++++++ ...Metadata_isolatedModules(module=esnext).js | 92 +++++++++++++++++ ...ata_isolatedModules(module=esnext).symbols | 72 ++++++++++++++ ...adata_isolatedModules(module=esnext).types | 74 ++++++++++++++ .../emitDecoratorMetadata_isolatedModules.ts | 1 + 9 files changed, 532 insertions(+) create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols create mode 100644 tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 38e989d67e424..325dc898515e9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33464,6 +33464,7 @@ namespace ts { } else if (forDecoratorMetadata && compilerOptions.isolatedModules + && getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015 && !symbolIsValue(rootSymbol) && !some(rootSymbol.declarations, isTypeOnlyImportOrExportDeclaration)) { const diag = error(typeName, Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_when_isolatedModules_and_emitDecoratorMetadata_are_enabled); diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js new file mode 100644 index 0000000000000..bb5e71383f494 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js @@ -0,0 +1,99 @@ +//// [tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts] //// + +//// [type1.ts] +interface T1 {} +export type { T1 } + +//// [type2.ts] +export interface T2 {} + +//// [class3.ts] +export class C3 {} + +//// [index.ts] +import { T1 } from "./type1"; +import type { T2 } from "./type2"; +import { C3 } from "./class3"; +declare var EventListener: any; + +class HelloWorld { + @EventListener('1') + handleEvent1(event: T1) {} // Error + + @EventListener('2') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + + @EventListener('2') + p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +} + + +//// [type1.js] +"use strict"; +exports.__esModule = true; +//// [type2.js] +"use strict"; +exports.__esModule = true; +//// [class3.js] +"use strict"; +exports.__esModule = true; +exports.C3 = void 0; +var C3 = /** @class */ (function () { + function C3() { + } + return C3; +}()); +exports.C3 = C3; +//// [index.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); +}; +exports.__esModule = true; +var class3_1 = require("./class3"); +var HelloWorld = /** @class */ (function () { + function HelloWorld() { + } + HelloWorld.prototype.handleEvent1 = function (event) { }; // Error + HelloWorld.prototype.handleEvent2 = function (event) { }; // Ok + HelloWorld.prototype.handleEvent3 = function (event) { return undefined; }; // Ok, Error + __decorate([ + EventListener('1'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], HelloWorld.prototype, "handleEvent1"); + __decorate([ + EventListener('2'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], HelloWorld.prototype, "handleEvent2"); + __decorate([ + EventListener('1'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p1"); + __decorate([ + EventListener('2'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p2"); + __decorate([ + EventListener('3'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [class3_1.C3]), + __metadata("design:returntype", Object) + ], HelloWorld.prototype, "handleEvent3"); + return HelloWorld; +}()); diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols new file mode 100644 index 0000000000000..c957b1781e3ea --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols @@ -0,0 +1,72 @@ +=== tests/cases/compiler/type1.ts === +interface T1 {} +>T1 : Symbol(T1, Decl(type1.ts, 0, 0)) + +export type { T1 } +>T1 : Symbol(T1, Decl(type1.ts, 1, 13)) + +=== tests/cases/compiler/type2.ts === +export interface T2 {} +>T2 : Symbol(T2, Decl(type2.ts, 0, 0)) + +=== tests/cases/compiler/class3.ts === +export class C3 {} +>C3 : Symbol(C3, Decl(class3.ts, 0, 0)) + +=== tests/cases/compiler/index.ts === +import { T1 } from "./type1"; +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + +import type { T2 } from "./type2"; +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + +import { C3 } from "./class3"; +>C3 : Symbol(C3, Decl(index.ts, 2, 8)) + +declare var EventListener: any; +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + +class HelloWorld { +>HelloWorld : Symbol(HelloWorld, Decl(index.ts, 3, 31)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent1(event: T1) {} // Error +>handleEvent1 : Symbol(HelloWorld.handleEvent1, Decl(index.ts, 5, 18)) +>event : Symbol(event, Decl(index.ts, 7, 15)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + + @EventListener('2') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent2(event: T2) {} // Ok +>handleEvent2 : Symbol(HelloWorld.handleEvent2, Decl(index.ts, 7, 28)) +>event : Symbol(event, Decl(index.ts, 10, 15)) +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + p1!: T1; // Error +>p1 : Symbol(HelloWorld.p1, Decl(index.ts, 10, 28)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + + @EventListener('2') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + p2!: T2; // Ok +>p2 : Symbol(HelloWorld.p2, Decl(index.ts, 13, 10)) +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + + @EventListener('3') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>handleEvent3 : Symbol(HelloWorld.handleEvent3, Decl(index.ts, 16, 10)) +>event : Symbol(event, Decl(index.ts, 19, 15)) +>C3 : Symbol(C3, Decl(index.ts, 2, 8)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) +>undefined : Symbol(undefined) +} + diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types new file mode 100644 index 0000000000000..0048771bb4eac --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types @@ -0,0 +1,74 @@ +=== tests/cases/compiler/type1.ts === +interface T1 {} +export type { T1 } +>T1 : T1 + +=== tests/cases/compiler/type2.ts === +export interface T2 {} +No type information for this code. +No type information for this code.=== tests/cases/compiler/class3.ts === +export class C3 {} +>C3 : C3 + +=== tests/cases/compiler/index.ts === +import { T1 } from "./type1"; +>T1 : any + +import type { T2 } from "./type2"; +>T2 : T2 + +import { C3 } from "./class3"; +>C3 : typeof C3 + +declare var EventListener: any; +>EventListener : any + +class HelloWorld { +>HelloWorld : HelloWorld + + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + handleEvent1(event: T1) {} // Error +>handleEvent1 : (event: T1) => void +>event : T1 + + @EventListener('2') +>EventListener('2') : any +>EventListener : any +>'2' : "2" + + handleEvent2(event: T2) {} // Ok +>handleEvent2 : (event: T2) => void +>event : T2 + + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + p1!: T1; // Error +>p1 : T1 + + @EventListener('2') +>EventListener('2') : any +>EventListener : any +>'2' : "2" + + p2!: T2; // Ok +>p2 : T2 + + @EventListener('3') +>EventListener('3') : any +>EventListener : any +>'3' : "3" + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>handleEvent3 : (event: C3) => T1 +>event : C3 +>undefined! : undefined +>undefined : undefined +} + diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt new file mode 100644 index 0000000000000..cabda9caa7e94 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt @@ -0,0 +1,47 @@ +tests/cases/compiler/index.ts(8,23): error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(14,8): error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(20,28): error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. + + +==== tests/cases/compiler/type1.ts (0 errors) ==== + interface T1 {} + export type { T1 } + +==== tests/cases/compiler/type2.ts (0 errors) ==== + export interface T2 {} + +==== tests/cases/compiler/class3.ts (0 errors) ==== + export class C3 {} + +==== tests/cases/compiler/index.ts (3 errors) ==== + import { T1 } from "./type1"; + import type { T2 } from "./type2"; + import { C3 } from "./class3"; + declare var EventListener: any; + + class HelloWorld { + @EventListener('1') + handleEvent1(event: T1) {} // Error + ~~ +!!! error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. + + @EventListener('2') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + ~~ +!!! error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. + + @EventListener('2') + p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error + ~~ +!!! error TS1267: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. + } + \ No newline at end of file diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js new file mode 100644 index 0000000000000..826cebf86b210 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js @@ -0,0 +1,92 @@ +//// [tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts] //// + +//// [type1.ts] +interface T1 {} +export type { T1 } + +//// [type2.ts] +export interface T2 {} + +//// [class3.ts] +export class C3 {} + +//// [index.ts] +import { T1 } from "./type1"; +import type { T2 } from "./type2"; +import { C3 } from "./class3"; +declare var EventListener: any; + +class HelloWorld { + @EventListener('1') + handleEvent1(event: T1) {} // Error + + @EventListener('2') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + + @EventListener('2') + p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +} + + +//// [type1.js] +export {}; +//// [type2.js] +export {}; +//// [class3.js] +var C3 = /** @class */ (function () { + function C3() { + } + return C3; +}()); +export { C3 }; +//// [index.js] +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); +}; +import { C3 } from "./class3"; +var HelloWorld = /** @class */ (function () { + function HelloWorld() { + } + HelloWorld.prototype.handleEvent1 = function (event) { }; // Error + HelloWorld.prototype.handleEvent2 = function (event) { }; // Ok + HelloWorld.prototype.handleEvent3 = function (event) { return undefined; }; // Ok, Error + __decorate([ + EventListener('1'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], HelloWorld.prototype, "handleEvent1"); + __decorate([ + EventListener('2'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], HelloWorld.prototype, "handleEvent2"); + __decorate([ + EventListener('1'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p1"); + __decorate([ + EventListener('2'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p2"); + __decorate([ + EventListener('3'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [C3]), + __metadata("design:returntype", Object) + ], HelloWorld.prototype, "handleEvent3"); + return HelloWorld; +}()); diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols new file mode 100644 index 0000000000000..c957b1781e3ea --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols @@ -0,0 +1,72 @@ +=== tests/cases/compiler/type1.ts === +interface T1 {} +>T1 : Symbol(T1, Decl(type1.ts, 0, 0)) + +export type { T1 } +>T1 : Symbol(T1, Decl(type1.ts, 1, 13)) + +=== tests/cases/compiler/type2.ts === +export interface T2 {} +>T2 : Symbol(T2, Decl(type2.ts, 0, 0)) + +=== tests/cases/compiler/class3.ts === +export class C3 {} +>C3 : Symbol(C3, Decl(class3.ts, 0, 0)) + +=== tests/cases/compiler/index.ts === +import { T1 } from "./type1"; +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + +import type { T2 } from "./type2"; +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + +import { C3 } from "./class3"; +>C3 : Symbol(C3, Decl(index.ts, 2, 8)) + +declare var EventListener: any; +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + +class HelloWorld { +>HelloWorld : Symbol(HelloWorld, Decl(index.ts, 3, 31)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent1(event: T1) {} // Error +>handleEvent1 : Symbol(HelloWorld.handleEvent1, Decl(index.ts, 5, 18)) +>event : Symbol(event, Decl(index.ts, 7, 15)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + + @EventListener('2') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent2(event: T2) {} // Ok +>handleEvent2 : Symbol(HelloWorld.handleEvent2, Decl(index.ts, 7, 28)) +>event : Symbol(event, Decl(index.ts, 10, 15)) +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + p1!: T1; // Error +>p1 : Symbol(HelloWorld.p1, Decl(index.ts, 10, 28)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) + + @EventListener('2') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + p2!: T2; // Ok +>p2 : Symbol(HelloWorld.p2, Decl(index.ts, 13, 10)) +>T2 : Symbol(T2, Decl(index.ts, 1, 13)) + + @EventListener('3') +>EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>handleEvent3 : Symbol(HelloWorld.handleEvent3, Decl(index.ts, 16, 10)) +>event : Symbol(event, Decl(index.ts, 19, 15)) +>C3 : Symbol(C3, Decl(index.ts, 2, 8)) +>T1 : Symbol(T1, Decl(index.ts, 0, 8)) +>undefined : Symbol(undefined) +} + diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types new file mode 100644 index 0000000000000..0048771bb4eac --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types @@ -0,0 +1,74 @@ +=== tests/cases/compiler/type1.ts === +interface T1 {} +export type { T1 } +>T1 : T1 + +=== tests/cases/compiler/type2.ts === +export interface T2 {} +No type information for this code. +No type information for this code.=== tests/cases/compiler/class3.ts === +export class C3 {} +>C3 : C3 + +=== tests/cases/compiler/index.ts === +import { T1 } from "./type1"; +>T1 : any + +import type { T2 } from "./type2"; +>T2 : T2 + +import { C3 } from "./class3"; +>C3 : typeof C3 + +declare var EventListener: any; +>EventListener : any + +class HelloWorld { +>HelloWorld : HelloWorld + + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + handleEvent1(event: T1) {} // Error +>handleEvent1 : (event: T1) => void +>event : T1 + + @EventListener('2') +>EventListener('2') : any +>EventListener : any +>'2' : "2" + + handleEvent2(event: T2) {} // Ok +>handleEvent2 : (event: T2) => void +>event : T2 + + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + p1!: T1; // Error +>p1 : T1 + + @EventListener('2') +>EventListener('2') : any +>EventListener : any +>'2' : "2" + + p2!: T2; // Ok +>p2 : T2 + + @EventListener('3') +>EventListener('3') : any +>EventListener : any +>'3' : "3" + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>handleEvent3 : (event: C3) => T1 +>event : C3 +>undefined! : undefined +>undefined : undefined +} + diff --git a/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts index 324d6218abda5..54143d508bebe 100644 --- a/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts +++ b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts @@ -1,6 +1,7 @@ // @experimentalDecorators: true // @emitDecoratorMetadata: true // @isolatedModules: true +// @module: commonjs,esnext // @Filename: type1.ts interface T1 {} From a8ee539464a5379804051000977502824995f854 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 2 Aug 2021 17:04:27 -0700 Subject: [PATCH 4/6] Add namespace import to error message as workaround --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- ...tadata_isolatedModules(module=commonjs).js | 9 ++++ ...a_isolatedModules(module=commonjs).symbols | 51 +++++++++++-------- ...ata_isolatedModules(module=commonjs).types | 12 +++++ ..._isolatedModules(module=esnext).errors.txt | 16 +++--- ...Metadata_isolatedModules(module=esnext).js | 9 ++++ ...ata_isolatedModules(module=esnext).symbols | 51 +++++++++++-------- ...adata_isolatedModules(module=esnext).types | 12 +++++ .../emitDecoratorMetadata_isolatedModules.ts | 4 ++ 10 files changed, 120 insertions(+), 48 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2f5591518a123..493b0a4fa4868 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34861,7 +34861,7 @@ namespace ts { && getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015 && !symbolIsValue(rootSymbol) && !some(rootSymbol.declarations, isTypeOnlyImportOrExportDeclaration)) { - const diag = error(typeName, Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_when_isolatedModules_and_emitDecoratorMetadata_are_enabled); + const diag = error(typeName, Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled); const aliasDeclaration = find(rootSymbol.declarations || emptyArray, isAliasSymbolDeclaration); if (aliasDeclaration) { addRelatedInfo(diag, createDiagnosticForNode(aliasDeclaration, Diagnostics._0_was_imported_here, idText(rootName))); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3d191f43a83fd..a2a7a7e68f0f6 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -891,7 +891,7 @@ "category": "Error", "code": 1268 }, - "A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled.": { + "A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled.": { "category": "Error", "code": 1269 }, diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js index bb5e71383f494..83510c982892d 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js @@ -12,6 +12,7 @@ export class C3 {} //// [index.ts] import { T1 } from "./type1"; +import * as t1 from "./type1"; import type { T2 } from "./type2"; import { C3 } from "./class3"; declare var EventListener: any; @@ -26,6 +27,9 @@ class HelloWorld { @EventListener('1') p1!: T1; // Error + @EventListener('1') + p1_ns!: t1.T1; // Ok + @EventListener('2') p2!: T2; // Ok @@ -62,6 +66,7 @@ var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; exports.__esModule = true; +var t1 = require("./type1"); var class3_1 = require("./class3"); var HelloWorld = /** @class */ (function () { function HelloWorld() { @@ -85,6 +90,10 @@ var HelloWorld = /** @class */ (function () { EventListener('1'), __metadata("design:type", Object) ], HelloWorld.prototype, "p1"); + __decorate([ + EventListener('1'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p1_ns"); __decorate([ EventListener('2'), __metadata("design:type", Object) diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols index c957b1781e3ea..075bdc3ee7822 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols @@ -17,55 +17,66 @@ export class C3 {} import { T1 } from "./type1"; >T1 : Symbol(T1, Decl(index.ts, 0, 8)) +import * as t1 from "./type1"; +>t1 : Symbol(t1, Decl(index.ts, 1, 6)) + import type { T2 } from "./type2"; ->T2 : Symbol(T2, Decl(index.ts, 1, 13)) +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) import { C3 } from "./class3"; ->C3 : Symbol(C3, Decl(index.ts, 2, 8)) +>C3 : Symbol(C3, Decl(index.ts, 3, 8)) declare var EventListener: any; ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) class HelloWorld { ->HelloWorld : Symbol(HelloWorld, Decl(index.ts, 3, 31)) +>HelloWorld : Symbol(HelloWorld, Decl(index.ts, 4, 31)) @EventListener('1') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) handleEvent1(event: T1) {} // Error ->handleEvent1 : Symbol(HelloWorld.handleEvent1, Decl(index.ts, 5, 18)) ->event : Symbol(event, Decl(index.ts, 7, 15)) +>handleEvent1 : Symbol(HelloWorld.handleEvent1, Decl(index.ts, 6, 18)) +>event : Symbol(event, Decl(index.ts, 8, 15)) >T1 : Symbol(T1, Decl(index.ts, 0, 8)) @EventListener('2') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) handleEvent2(event: T2) {} // Ok ->handleEvent2 : Symbol(HelloWorld.handleEvent2, Decl(index.ts, 7, 28)) ->event : Symbol(event, Decl(index.ts, 10, 15)) ->T2 : Symbol(T2, Decl(index.ts, 1, 13)) +>handleEvent2 : Symbol(HelloWorld.handleEvent2, Decl(index.ts, 8, 28)) +>event : Symbol(event, Decl(index.ts, 11, 15)) +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) @EventListener('1') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) p1!: T1; // Error ->p1 : Symbol(HelloWorld.p1, Decl(index.ts, 10, 28)) +>p1 : Symbol(HelloWorld.p1, Decl(index.ts, 11, 28)) >T1 : Symbol(T1, Decl(index.ts, 0, 8)) + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) + + p1_ns!: t1.T1; // Ok +>p1_ns : Symbol(HelloWorld.p1_ns, Decl(index.ts, 14, 10)) +>t1 : Symbol(t1, Decl(index.ts, 1, 6)) +>T1 : Symbol(t1.T1, Decl(type1.ts, 1, 13)) + @EventListener('2') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) p2!: T2; // Ok ->p2 : Symbol(HelloWorld.p2, Decl(index.ts, 13, 10)) ->T2 : Symbol(T2, Decl(index.ts, 1, 13)) +>p2 : Symbol(HelloWorld.p2, Decl(index.ts, 17, 16)) +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) @EventListener('3') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) handleEvent3(event: C3): T1 { return undefined! } // Ok, Error ->handleEvent3 : Symbol(HelloWorld.handleEvent3, Decl(index.ts, 16, 10)) ->event : Symbol(event, Decl(index.ts, 19, 15)) ->C3 : Symbol(C3, Decl(index.ts, 2, 8)) +>handleEvent3 : Symbol(HelloWorld.handleEvent3, Decl(index.ts, 20, 10)) +>event : Symbol(event, Decl(index.ts, 23, 15)) +>C3 : Symbol(C3, Decl(index.ts, 3, 8)) >T1 : Symbol(T1, Decl(index.ts, 0, 8)) >undefined : Symbol(undefined) } diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types index 0048771bb4eac..b2615eac81b73 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types @@ -14,6 +14,9 @@ export class C3 {} import { T1 } from "./type1"; >T1 : any +import * as t1 from "./type1"; +>t1 : typeof t1 + import type { T2 } from "./type2"; >T2 : T2 @@ -52,6 +55,15 @@ class HelloWorld { p1!: T1; // Error >p1 : T1 + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + p1_ns!: t1.T1; // Ok +>p1_ns : T1 +>t1 : any + @EventListener('2') >EventListener('2') : any >EventListener : any diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt index d8f87d0074e30..79c48aa00d806 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt @@ -1,6 +1,6 @@ -tests/cases/compiler/index.ts(8,23): error TS1269: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. -tests/cases/compiler/index.ts(14,8): error TS1269: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. -tests/cases/compiler/index.ts(20,28): error TS1269: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(9,23): error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(15,8): error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(24,28): error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. ==== tests/cases/compiler/type1.ts (0 errors) ==== @@ -15,6 +15,7 @@ tests/cases/compiler/index.ts(20,28): error TS1269: A type referenced in a decor ==== tests/cases/compiler/index.ts (3 errors) ==== import { T1 } from "./type1"; + import * as t1 from "./type1"; import type { T2 } from "./type2"; import { C3 } from "./class3"; declare var EventListener: any; @@ -23,7 +24,7 @@ tests/cases/compiler/index.ts(20,28): error TS1269: A type referenced in a decor @EventListener('1') handleEvent1(event: T1) {} // Error ~~ -!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. !!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. @EventListener('2') @@ -32,16 +33,19 @@ tests/cases/compiler/index.ts(20,28): error TS1269: A type referenced in a decor @EventListener('1') p1!: T1; // Error ~~ -!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. !!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. + @EventListener('1') + p1_ns!: t1.T1; // Ok + @EventListener('2') p2!: T2; // Ok @EventListener('3') handleEvent3(event: C3): T1 { return undefined! } // Ok, Error ~~ -!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. !!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. } \ No newline at end of file diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js index 826cebf86b210..121f3be7031c6 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js @@ -12,6 +12,7 @@ export class C3 {} //// [index.ts] import { T1 } from "./type1"; +import * as t1 from "./type1"; import type { T2 } from "./type2"; import { C3 } from "./class3"; declare var EventListener: any; @@ -26,6 +27,9 @@ class HelloWorld { @EventListener('1') p1!: T1; // Error + @EventListener('1') + p1_ns!: t1.T1; // Ok + @EventListener('2') p2!: T2; // Ok @@ -55,6 +59,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; +import * as t1 from "./type1"; import { C3 } from "./class3"; var HelloWorld = /** @class */ (function () { function HelloWorld() { @@ -78,6 +83,10 @@ var HelloWorld = /** @class */ (function () { EventListener('1'), __metadata("design:type", Object) ], HelloWorld.prototype, "p1"); + __decorate([ + EventListener('1'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p1_ns"); __decorate([ EventListener('2'), __metadata("design:type", Object) diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols index c957b1781e3ea..075bdc3ee7822 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols @@ -17,55 +17,66 @@ export class C3 {} import { T1 } from "./type1"; >T1 : Symbol(T1, Decl(index.ts, 0, 8)) +import * as t1 from "./type1"; +>t1 : Symbol(t1, Decl(index.ts, 1, 6)) + import type { T2 } from "./type2"; ->T2 : Symbol(T2, Decl(index.ts, 1, 13)) +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) import { C3 } from "./class3"; ->C3 : Symbol(C3, Decl(index.ts, 2, 8)) +>C3 : Symbol(C3, Decl(index.ts, 3, 8)) declare var EventListener: any; ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) class HelloWorld { ->HelloWorld : Symbol(HelloWorld, Decl(index.ts, 3, 31)) +>HelloWorld : Symbol(HelloWorld, Decl(index.ts, 4, 31)) @EventListener('1') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) handleEvent1(event: T1) {} // Error ->handleEvent1 : Symbol(HelloWorld.handleEvent1, Decl(index.ts, 5, 18)) ->event : Symbol(event, Decl(index.ts, 7, 15)) +>handleEvent1 : Symbol(HelloWorld.handleEvent1, Decl(index.ts, 6, 18)) +>event : Symbol(event, Decl(index.ts, 8, 15)) >T1 : Symbol(T1, Decl(index.ts, 0, 8)) @EventListener('2') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) handleEvent2(event: T2) {} // Ok ->handleEvent2 : Symbol(HelloWorld.handleEvent2, Decl(index.ts, 7, 28)) ->event : Symbol(event, Decl(index.ts, 10, 15)) ->T2 : Symbol(T2, Decl(index.ts, 1, 13)) +>handleEvent2 : Symbol(HelloWorld.handleEvent2, Decl(index.ts, 8, 28)) +>event : Symbol(event, Decl(index.ts, 11, 15)) +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) @EventListener('1') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) p1!: T1; // Error ->p1 : Symbol(HelloWorld.p1, Decl(index.ts, 10, 28)) +>p1 : Symbol(HelloWorld.p1, Decl(index.ts, 11, 28)) >T1 : Symbol(T1, Decl(index.ts, 0, 8)) + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) + + p1_ns!: t1.T1; // Ok +>p1_ns : Symbol(HelloWorld.p1_ns, Decl(index.ts, 14, 10)) +>t1 : Symbol(t1, Decl(index.ts, 1, 6)) +>T1 : Symbol(t1.T1, Decl(type1.ts, 1, 13)) + @EventListener('2') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) p2!: T2; // Ok ->p2 : Symbol(HelloWorld.p2, Decl(index.ts, 13, 10)) ->T2 : Symbol(T2, Decl(index.ts, 1, 13)) +>p2 : Symbol(HelloWorld.p2, Decl(index.ts, 17, 16)) +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) @EventListener('3') ->EventListener : Symbol(EventListener, Decl(index.ts, 3, 11)) +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) handleEvent3(event: C3): T1 { return undefined! } // Ok, Error ->handleEvent3 : Symbol(HelloWorld.handleEvent3, Decl(index.ts, 16, 10)) ->event : Symbol(event, Decl(index.ts, 19, 15)) ->C3 : Symbol(C3, Decl(index.ts, 2, 8)) +>handleEvent3 : Symbol(HelloWorld.handleEvent3, Decl(index.ts, 20, 10)) +>event : Symbol(event, Decl(index.ts, 23, 15)) +>C3 : Symbol(C3, Decl(index.ts, 3, 8)) >T1 : Symbol(T1, Decl(index.ts, 0, 8)) >undefined : Symbol(undefined) } diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types index 0048771bb4eac..b2615eac81b73 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types @@ -14,6 +14,9 @@ export class C3 {} import { T1 } from "./type1"; >T1 : any +import * as t1 from "./type1"; +>t1 : typeof t1 + import type { T2 } from "./type2"; >T2 : T2 @@ -52,6 +55,15 @@ class HelloWorld { p1!: T1; // Error >p1 : T1 + @EventListener('1') +>EventListener('1') : any +>EventListener : any +>'1' : "1" + + p1_ns!: t1.T1; // Ok +>p1_ns : T1 +>t1 : any + @EventListener('2') >EventListener('2') : any >EventListener : any diff --git a/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts index 54143d508bebe..e90af623cc12d 100644 --- a/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts +++ b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts @@ -15,6 +15,7 @@ export class C3 {} // @Filename: index.ts import { T1 } from "./type1"; +import * as t1 from "./type1"; import type { T2 } from "./type2"; import { C3 } from "./class3"; declare var EventListener: any; @@ -29,6 +30,9 @@ class HelloWorld { @EventListener('1') p1!: T1; // Error + @EventListener('1') + p1_ns!: t1.T1; // Ok + @EventListener('2') p2!: T2; // Ok From 00004a719c73efac7191974361f5481e3f044dbc Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 3 Aug 2021 12:24:38 -0700 Subject: [PATCH 5/6] Add codefix --- src/compiler/diagnosticMessages.json | 1 + .../fixUnreferenceableDecoratorMetadata.ts | 70 +++++++++++++++++++ src/services/refactors/convertImport.ts | 2 +- src/services/tsconfig.json | 1 + ...odefixUnreferenceableDecoratorMetadata1.ts | 46 ++++++++++++ ...odefixUnreferenceableDecoratorMetadata2.ts | 38 ++++++++++ ...odefixUnreferenceableDecoratorMetadata3.ts | 55 +++++++++++++++ 7 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts create mode 100644 tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata1.ts create mode 100644 tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata2.ts create mode 100644 tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata3.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a2a7a7e68f0f6..069c47c869f62 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7115,6 +7115,7 @@ "code": 95166 }, + "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", "code": 18004 diff --git a/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts b/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts new file mode 100644 index 0000000000000..5aa83bb8382b2 --- /dev/null +++ b/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts @@ -0,0 +1,70 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "fixUnreferenceableDecoratorMetadata"; + const errorCodes = [Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled.code]; + registerCodeFix({ + errorCodes, + getCodeActions: context => { + const importDeclaration = getImportDeclaration(context.sourceFile, context.program, context.span.start); + if (!importDeclaration) return; + + const namespaceChanges = textChanges.ChangeTracker.with(context, t => importDeclaration.kind === SyntaxKind.ImportSpecifier && doNamespaceImportChange(t, context.sourceFile, importDeclaration, context.program)); + const typeOnlyChanges = textChanges.ChangeTracker.with(context, t => doTypeOnlyImportChange(t, context.sourceFile, importDeclaration, context.program)); + let actions: CodeFixAction[] | undefined; + if (namespaceChanges.length) { + actions = append(actions, createCodeFixActionWithoutFixAll(fixId, namespaceChanges, Diagnostics.Convert_named_imports_to_namespace_import)); + } + if (typeOnlyChanges.length) { + actions = append(actions, createCodeFixActionWithoutFixAll(fixId, typeOnlyChanges, Diagnostics.Convert_to_type_only_import)); + } + return actions; + }, + fixIds: [fixId], + }); + + function getImportDeclaration(sourceFile: SourceFile, program: Program, start: number): ImportClause | ImportSpecifier | ImportEqualsDeclaration | undefined { + const identifier = tryCast(getTokenAtPosition(sourceFile, start), isIdentifier); + if (!identifier || identifier.parent.kind !== SyntaxKind.TypeReference) return; + + const checker = program.getTypeChecker(); + const symbol = checker.getSymbolAtLocation(identifier); + return find(symbol?.declarations || emptyArray, or(isImportClause, isImportSpecifier, isImportEqualsDeclaration) as (n: Node) => n is ImportClause | ImportSpecifier | ImportEqualsDeclaration); + } + + // Converts the import declaration of the offending import to a type-only import, + // only if it can be done without affecting other imported names. If the conversion + // cannot be done cleanly, we could offer to *extract* the offending import to a + // new type-only import declaration, but honestly I doubt anyone will ever use this + // codefix at all, so it's probably not worth the lines of code. + function doTypeOnlyImportChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, importDeclaration: ImportClause | ImportSpecifier | ImportEqualsDeclaration, program: Program) { + if (importDeclaration.kind === SyntaxKind.ImportEqualsDeclaration) { + changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, importDeclaration.name); + return; + } + + const importClause = importDeclaration.kind === SyntaxKind.ImportClause ? importDeclaration : importDeclaration.parent.parent; + if (importClause.name && importClause.namedBindings) { + // Cannot convert an import with a default import and named bindings to type-only + // (it's a grammar error). + return; + } + + const checker = program.getTypeChecker(); + const importsValue = !!forEachImportClauseDeclaration(importClause, decl => { + if (skipAlias(decl.symbol, checker).flags & SymbolFlags.Value) return true; + }); + + if (importsValue) { + // Assume that if someone wrote a non-type-only import that includes some values, + // they intend to use those values in value positions, even if they haven't yet. + // Don't convert it to type-only. + return; + } + + changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, importClause); + } + + function doNamespaceImportChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, importDeclaration: ImportSpecifier, program: Program) { + refactor.doChangeNamedToNamespace(sourceFile, program.getTypeChecker(), changes, importDeclaration.parent); + } +} diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 848d37c7a5945..7cc8879fa5186 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -134,7 +134,7 @@ namespace ts.refactor { return isPropertyAccessExpression(propertyAccessOrQualifiedName) ? propertyAccessOrQualifiedName.expression : propertyAccessOrQualifiedName.left; } - function doChangeNamedToNamespace(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamedImports): void { + export function doChangeNamedToNamespace(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamedImports): void { const importDecl = toConvert.parent.parent; const { moduleSpecifier } = importDecl; diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index f1aa438fefb57..ea92329cf1cbb 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -86,6 +86,7 @@ "codefixes/fixExtendsInterfaceBecomesImplements.ts", "codefixes/fixForgottenThisPropertyAccess.ts", "codefixes/fixInvalidJsxCharacters.ts", + "codefixes/fixUnreferenceableDecoratorMetadata.ts", "codefixes/fixUnusedIdentifier.ts", "codefixes/fixUnreachableCode.ts", "codefixes/fixUnusedLabel.ts", diff --git a/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata1.ts b/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata1.ts new file mode 100644 index 0000000000000..40f9a7239a762 --- /dev/null +++ b/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata1.ts @@ -0,0 +1,46 @@ +/// + +// @isolatedModules: true +// @module: es2015 +// @experimentalDecorators: true +// @emitDecoratorMetadata: true + +// @Filename: /mod.ts +//// export default interface I1 {} +//// export interface I2 {} + +// @Filename: /index.ts +//// [|import { I2 } from "./mod";|] +//// +//// declare var EventListener: any; +//// class HelloWorld { +//// @EventListener("1") +//// p1!: I2; +//// p2!: I2; +//// } + +const diag = ts.Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled; + +goTo.file("/index.ts"); + +verify.codeFix({ + index: 0, + description: ts.Diagnostics.Convert_named_imports_to_namespace_import.message, + errorCode: diag.code, + applyChanges: false, + newFileContent: `import * as mod from "./mod"; + +declare var EventListener: any; +class HelloWorld { + @EventListener("1") + p1!: mod.I2; + p2!: mod.I2; +}`, +}); + +verify.codeFix({ + index: 1, + description: ts.Diagnostics.Convert_to_type_only_import.message, + errorCode: diag.code, + newRangeContent: `import type { I2 } from "./mod";`, +}); diff --git a/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata2.ts b/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata2.ts new file mode 100644 index 0000000000000..1ab14d1fc6a42 --- /dev/null +++ b/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata2.ts @@ -0,0 +1,38 @@ +/// + +// @isolatedModules: true +// @module: es2015 +// @experimentalDecorators: true +// @emitDecoratorMetadata: true + +// @Filename: /mod.ts +//// export default interface I1 {} +//// export interface I2 {} + +// @Filename: /index.ts +//// [|import I1, { I2 } from "./mod";|] +//// +//// declare var EventListener: any; +//// class HelloWorld { +//// @EventListener("1") +//// p1!: I2; +//// p2!: I2; +//// } + +const diag = ts.Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled; + +goTo.file("/index.ts"); + +verify.codeFix({ + description: ts.Diagnostics.Convert_named_imports_to_namespace_import.message, + errorCode: diag.code, + applyChanges: false, + newFileContent: `import I1, * as mod from "./mod"; + +declare var EventListener: any; +class HelloWorld { + @EventListener("1") + p1!: mod.I2; + p2!: mod.I2; +}`, +}); diff --git a/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata3.ts b/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata3.ts new file mode 100644 index 0000000000000..7a7c66917c81e --- /dev/null +++ b/tests/cases/fourslash/codefixUnreferenceableDecoratorMetadata3.ts @@ -0,0 +1,55 @@ +/// + +// @isolatedModules: true +// @module: es2015 +// @experimentalDecorators: true +// @emitDecoratorMetadata: true + +// @Filename: /mod.ts +//// export default interface I1 {} +//// export interface I2 {} +//// export class C1 {} + +// @Filename: /index.ts +//// import I1, { I2 } from "./mod"; +//// +//// declare var EventListener: any; +//// export class HelloWorld { +//// @EventListener("1") +//// p1!: I1; +//// p2!: I2; +//// } + +// @Filename: /index2.ts +//// import { C1, I2 } from "./mod"; +//// +//// declare var EventListener: any; +//// export class HelloWorld { +//// @EventListener("1") +//// p1!: I2; +//// p2!: C1; +//// } + +const diag = ts.Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled; + +goTo.file("/index.ts"); +verify.not.codeFixAvailable(); + +// Mostly verifying that the type-only fix is not available +// (if both were available you'd have to specify `index` +// in `verify.codeFix`). +goTo.file("/index2.ts"); +verify.codeFix({ + description: ts.Diagnostics.Convert_named_imports_to_namespace_import.message, + errorCode: diag.code, + applyChanges: false, + newFileContent: `import * as mod from "./mod"; + +declare var EventListener: any; +export class HelloWorld { + @EventListener("1") + p1!: mod.I2; + p2!: mod.C1; +}`, +}); + From cb66b005a8c897a1dddfd50b35a68eefb534f1cc Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 17 Mar 2022 11:19:16 -0700 Subject: [PATCH 6/6] Fix merge fallout --- src/compiler/checker.ts | 2 +- .../fixUnreferenceableDecoratorMetadata.ts | 2 +- src/services/refactors/convertImport.ts | 14 +++++++++----- ...adata_isolatedModules(module=esnext).errors.txt | 12 ++++++------ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 77cf9da9c1767..0d7adfafede23 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36438,7 +36438,7 @@ namespace ts { const rootName = getFirstIdentifier(typeName); const meaning = (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias; - const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isRefernce*/ true); + const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isReference*/ true); if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias) { if (symbolIsValue(rootSymbol) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol)) diff --git a/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts b/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts index 5aa83bb8382b2..30fefde64efed 100644 --- a/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts +++ b/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts @@ -65,6 +65,6 @@ namespace ts.codefix { } function doNamespaceImportChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, importDeclaration: ImportSpecifier, program: Program) { - refactor.doChangeNamedToNamespace(sourceFile, program.getTypeChecker(), changes, importDeclaration.parent); + refactor.doChangeNamedToNamespaceOrDefault(sourceFile, program, changes, importDeclaration.parent); } } diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 6f373e6c3e2e6..01333162abb14 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -79,22 +79,25 @@ namespace ts.refactor { if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { return { convertTo: ImportKind.Named, import: importClause.namedBindings }; } - const compilerOptions = context.program.getCompilerOptions(); - const shouldUseDefault = getAllowSyntheticDefaultImports(compilerOptions) - && isExportEqualsModule(importClause.parent.moduleSpecifier, context.program.getTypeChecker()); + const shouldUseDefault = getShouldUseDefault(context.program, importClause); return shouldUseDefault ? { convertTo: ImportKind.Default, import: importClause.namedBindings } : { convertTo: ImportKind.Namespace, import: importClause.namedBindings }; } + function getShouldUseDefault(program: Program, importClause: ImportClause) { + return getAllowSyntheticDefaultImports(program.getCompilerOptions()) + && isExportEqualsModule(importClause.parent.moduleSpecifier, program.getTypeChecker()); + } + function doChange(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, info: ImportConversionInfo): void { const checker = program.getTypeChecker(); if (info.convertTo === ImportKind.Named) { doChangeNamespaceToNamed(sourceFile, checker, changes, info.import, getAllowSyntheticDefaultImports(program.getCompilerOptions())); } else { - doChangeNamedToNamespaceOrDefault(sourceFile, checker, changes, info.import, info.convertTo === ImportKind.Default); + doChangeNamedToNamespaceOrDefault(sourceFile, program, changes, info.import, info.convertTo === ImportKind.Default); } } @@ -153,7 +156,8 @@ namespace ts.refactor { return isPropertyAccessExpression(propertyAccessOrQualifiedName) ? propertyAccessOrQualifiedName.expression : propertyAccessOrQualifiedName.left; } - export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamedImports, shouldUseDefault: boolean) { + export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, toConvert: NamedImports, shouldUseDefault = getShouldUseDefault(program, toConvert.parent)): void { + const checker = program.getTypeChecker(); const importDecl = toConvert.parent.parent; const { moduleSpecifier } = importDecl; diff --git a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt index 79c48aa00d806..5e7f959a50696 100644 --- a/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt @@ -1,6 +1,6 @@ -tests/cases/compiler/index.ts(9,23): error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. -tests/cases/compiler/index.ts(15,8): error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. -tests/cases/compiler/index.ts(24,28): error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(9,23): error TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(15,8): error TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +tests/cases/compiler/index.ts(24,28): error TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. ==== tests/cases/compiler/type1.ts (0 errors) ==== @@ -24,7 +24,7 @@ tests/cases/compiler/index.ts(24,28): error TS1269: A type referenced in a decor @EventListener('1') handleEvent1(event: T1) {} // Error ~~ -!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! error TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. !!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. @EventListener('2') @@ -33,7 +33,7 @@ tests/cases/compiler/index.ts(24,28): error TS1269: A type referenced in a decor @EventListener('1') p1!: T1; // Error ~~ -!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! error TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. !!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. @EventListener('1') @@ -45,7 +45,7 @@ tests/cases/compiler/index.ts(24,28): error TS1269: A type referenced in a decor @EventListener('3') handleEvent3(event: C3): T1 { return undefined! } // Ok, Error ~~ -!!! error TS1269: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. +!!! error TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled. !!! related TS1376 tests/cases/compiler/index.ts:1:10: 'T1' was imported here. } \ No newline at end of file