From df1faa09b8b8609c110507743ee98ce64cc612b6 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 17 Mar 2022 12:00:23 -0700 Subject: [PATCH] Add isolatedModules error for ambiguous imports referenced in decorator metadata (#42915) * Add isolatedModules error for ambiguous imports referenced in decorator metadata * Improve test and accept baselines * Error only for es2015+ * Add namespace import to error message as workaround * Add codefix * Fix merge fallout --- src/compiler/checker.ts | 29 +++-- src/compiler/diagnosticMessages.json | 5 + .../fixUnreferenceableDecoratorMetadata.ts | 70 ++++++++++++ src/services/refactors/convertImport.ts | 14 ++- src/services/tsconfig.json | 1 + ...tadata_isolatedModules(module=commonjs).js | 108 ++++++++++++++++++ ...a_isolatedModules(module=commonjs).symbols | 83 ++++++++++++++ ...ata_isolatedModules(module=commonjs).types | 86 ++++++++++++++ ..._isolatedModules(module=esnext).errors.txt | 51 +++++++++ ...Metadata_isolatedModules(module=esnext).js | 101 ++++++++++++++++ ...ata_isolatedModules(module=esnext).symbols | 83 ++++++++++++++ ...adata_isolatedModules(module=esnext).types | 86 ++++++++++++++ ...coratorMetadata_isolatedModules.errors.txt | 47 ++++++++ .../emitDecoratorMetadata_isolatedModules.js | 99 ++++++++++++++++ ...tDecoratorMetadata_isolatedModules.symbols | 72 ++++++++++++ ...mitDecoratorMetadata_isolatedModules.types | 74 ++++++++++++ .../emitDecoratorMetadata_isolatedModules.ts | 41 +++++++ ...odefixUnreferenceableDecoratorMetadata1.ts | 46 ++++++++ ...odefixUnreferenceableDecoratorMetadata2.ts | 38 ++++++ ...odefixUnreferenceableDecoratorMetadata3.ts | 55 +++++++++ 20 files changed, 1175 insertions(+), 14 deletions(-) create mode 100644 src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts 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 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 create mode 100644 tests/cases/compiler/emitDecoratorMetadata_isolatedModules.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/checker.ts b/src/compiler/checker.ts index 82fc1f490a4f4..0d7adfafede23 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36430,21 +36430,32 @@ 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, /*isReference*/ 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 + && 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_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))); + } + } } } @@ -36458,7 +36469,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 cf86150ee3b24..affe89f5b2cb1 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -879,6 +879,10 @@ "category": "Error", "code": 1271 }, + "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": 1272 + }, "'with' statements are not allowed in an async function block.": { "category": "Error", @@ -7174,6 +7178,7 @@ "code": 95173 }, + "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..30fefde64efed --- /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.doChangeNamedToNamespaceOrDefault(sourceFile, program, changes, importDeclaration.parent); + } +} diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index 3589e3006c1bf..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; } - 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/src/services/tsconfig.json b/src/services/tsconfig.json index 3cd2188314613..cef6b9139698a 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -88,6 +88,7 @@ "codefixes/fixForgottenThisPropertyAccess.ts", "codefixes/fixInvalidJsxCharacters.ts", "codefixes/fixUnmatchedParameter.ts", + "codefixes/fixUnreferenceableDecoratorMetadata.ts", "codefixes/fixUnusedIdentifier.ts", "codefixes/fixUnreachableCode.ts", "codefixes/fixUnusedLabel.ts", 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..83510c982892d --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).js @@ -0,0 +1,108 @@ +//// [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 * as 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('1') + p1_ns!: t1.T1; // Ok + + @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 t1 = require("./type1"); +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('1'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p1_ns"); + __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..075bdc3ee7822 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).symbols @@ -0,0 +1,83 @@ +=== 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 * as t1 from "./type1"; +>t1 : Symbol(t1, Decl(index.ts, 1, 6)) + +import type { T2 } from "./type2"; +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) + +import { C3 } from "./class3"; +>C3 : Symbol(C3, Decl(index.ts, 3, 8)) + +declare var EventListener: any; +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) + +class HelloWorld { +>HelloWorld : Symbol(HelloWorld, Decl(index.ts, 4, 31)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) + + handleEvent1(event: T1) {} // Error +>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, 4, 11)) + + handleEvent2(event: T2) {} // Ok +>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, 4, 11)) + + p1!: T1; // Error +>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, 4, 11)) + + p2!: T2; // Ok +>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, 4, 11)) + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>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 new file mode 100644 index 0000000000000..b2615eac81b73 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=commonjs).types @@ -0,0 +1,86 @@ +=== 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 * as t1 from "./type1"; +>t1 : typeof t1 + +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('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 +>'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..5e7f959a50696 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).errors.txt @@ -0,0 +1,51 @@ +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) ==== + 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 * as 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 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') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + ~~ +!!! 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') + p1_ns!: t1.T1; // Ok + + @EventListener('2') + p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error + ~~ +!!! 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 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..121f3be7031c6 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).js @@ -0,0 +1,101 @@ +//// [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 * as 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('1') + p1_ns!: t1.T1; // Ok + + @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 * as t1 from "./type1"; +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('1'), + __metadata("design:type", Object) + ], HelloWorld.prototype, "p1_ns"); + __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..075bdc3ee7822 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).symbols @@ -0,0 +1,83 @@ +=== 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 * as t1 from "./type1"; +>t1 : Symbol(t1, Decl(index.ts, 1, 6)) + +import type { T2 } from "./type2"; +>T2 : Symbol(T2, Decl(index.ts, 2, 13)) + +import { C3 } from "./class3"; +>C3 : Symbol(C3, Decl(index.ts, 3, 8)) + +declare var EventListener: any; +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) + +class HelloWorld { +>HelloWorld : Symbol(HelloWorld, Decl(index.ts, 4, 31)) + + @EventListener('1') +>EventListener : Symbol(EventListener, Decl(index.ts, 4, 11)) + + handleEvent1(event: T1) {} // Error +>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, 4, 11)) + + handleEvent2(event: T2) {} // Ok +>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, 4, 11)) + + p1!: T1; // Error +>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, 4, 11)) + + p2!: T2; // Ok +>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, 4, 11)) + + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +>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 new file mode 100644 index 0000000000000..b2615eac81b73 --- /dev/null +++ b/tests/baselines/reference/emitDecoratorMetadata_isolatedModules(module=esnext).types @@ -0,0 +1,86 @@ +=== 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 * as t1 from "./type1"; +>t1 : typeof t1 + +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('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 +>'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.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 new file mode 100644 index 0000000000000..e90af623cc12d --- /dev/null +++ b/tests/cases/compiler/emitDecoratorMetadata_isolatedModules.ts @@ -0,0 +1,41 @@ +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +// @isolatedModules: true +// @module: commonjs,esnext + +// @Filename: type1.ts +interface T1 {} +export type { T1 } + +// @Filename: type2.ts +export interface T2 {} + +// @Filename: class3.ts +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; + +class HelloWorld { + @EventListener('1') + handleEvent1(event: T1) {} // Error + + @EventListener('2') + handleEvent2(event: T2) {} // Ok + + @EventListener('1') + p1!: T1; // Error + + @EventListener('1') + p1_ns!: t1.T1; // Ok + + @EventListener('2') + p2!: T2; // Ok + + @EventListener('3') + handleEvent3(event: C3): T1 { return undefined! } // Ok, Error +} 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; +}`, +}); +