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
This commit is contained in:
Andrew Branch 2022-03-17 12:00:23 -07:00 committed by GitHub
parent b996287e00
commit df1faa09b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1175 additions and 14 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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",

View File

@ -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;
}());

View File

@ -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)
}

View File

@ -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
}

View File

@ -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.
}

View File

@ -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;
}());

View File

@ -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)
}

View File

@ -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
}

View File

@ -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.
}

View File

@ -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;
}());

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -0,0 +1,46 @@
/// <reference path="fourslash.ts" />
// @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";`,
});

View File

@ -0,0 +1,38 @@
/// <reference path="fourslash.ts" />
// @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;
}`,
});

View File

@ -0,0 +1,55 @@
/// <reference path="fourslash.ts" />
// @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;
}`,
});