diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b0ea7ea9ffb04..9ccd81ada5204 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -735,6 +735,7 @@ import { isTypeNode, isTypeNodeKind, isTypeOfExpression, + isTypeOnlyImportDeclaration, isTypeOnlyImportOrExportDeclaration, isTypeOperatorNode, isTypeParameterDeclaration, @@ -3603,6 +3604,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ); } } + + // Look at 'compilerOptions.isolatedModules' and not 'getIsolatedModules(...)' (which considers 'verbatimModuleSyntax') + // here because 'verbatimModuleSyntax' will already have an error for importing a type without 'import type'. + if (compilerOptions.isolatedModules && result && isInExternalModule && (meaning & SymbolFlags.Value) === SymbolFlags.Value) { + const isGlobal = lookup(globals, name, meaning) === result; + const nonValueSymbol = isGlobal && isSourceFile(lastLocation!) && lastLocation.locals && lookup(lastLocation.locals, name, ~SymbolFlags.Value); + if (nonValueSymbol) { + const importDecl = nonValueSymbol.declarations?.find(d => d.kind === SyntaxKind.ImportSpecifier || d.kind === SyntaxKind.ImportClause || d.kind === SyntaxKind.NamespaceImport || d.kind === SyntaxKind.ImportEqualsDeclaration); + if (importDecl && !isTypeOnlyImportDeclaration(importDecl)) { + error(importDecl, Diagnostics.Import_0_conflicts_with_global_value_used_in_this_file_so_must_be_declared_with_a_type_only_import_when_isolatedModules_is_enabled, unescapeLeadingUnderscores(name)); + } + } + } }); } return result; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 80c55bd7ef277..8a16147b63381 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3731,6 +3731,10 @@ "category": "Error", "code": 2865 }, + "Import '{0}' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled.": { + "category": "Error", + "code": 2866 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=false,verbatimmodulesyntax=true).errors.txt b/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=false,verbatimmodulesyntax=true).errors.txt new file mode 100644 index 0000000000000..09ca32e0905e6 --- /dev/null +++ b/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=false,verbatimmodulesyntax=true).errors.txt @@ -0,0 +1,67 @@ +bad.ts(1,10): error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. +bad.ts(1,10): error TS1484: 'Date' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. +bad.ts(1,16): error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. +bad.ts(1,16): error TS1484: 'Event' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. +good.ts(2,10): error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + + +==== ./types.ts (0 errors) ==== + export interface Date { + day: number; + month: number; + year: number; + } + + export namespace Event { + export type T = any; + } + +==== ./node.d.ts (0 errors) ==== + declare module 'node:console' { + global { + interface Console { + Console: console.ConsoleConstructor; + } + namespace console { + interface ConsoleConstructor { + prototype: Console; + new (): Console; + } + } + var console: Console; + } + export = globalThis.console; + } + +==== ./bad.ts (4 errors) ==== + import { Date, Event } from './types'; + ~~~~ +!!! error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + ~~~~ +!!! error TS1484: 'Date' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. + ~~~~~ +!!! error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + ~~~~~ +!!! error TS1484: 'Event' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. + function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); + } + function bar() { + return new Event('bar') as Event.T; + } + +==== ./good.ts (1 errors) ==== + import type { Date, Event } from './types'; + import { Console } from 'node:console'; + ~~~~~~~ +!!! error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); + } + function bar() { + return new Event('bar') as Event.T; + } + const baz: Console = new Console(); + \ No newline at end of file diff --git a/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=false).errors.txt b/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=false).errors.txt new file mode 100644 index 0000000000000..2c91eac390a77 --- /dev/null +++ b/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=false).errors.txt @@ -0,0 +1,58 @@ +bad.ts(1,10): error TS2866: Import 'Date' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. +bad.ts(1,16): error TS2866: Import 'Event' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. + + +==== ./types.ts (0 errors) ==== + export interface Date { + day: number; + month: number; + year: number; + } + + export namespace Event { + export type T = any; + } + +==== ./node.d.ts (0 errors) ==== + declare module 'node:console' { + global { + interface Console { + Console: console.ConsoleConstructor; + } + namespace console { + interface ConsoleConstructor { + prototype: Console; + new (): Console; + } + } + var console: Console; + } + export = globalThis.console; + } + +==== ./bad.ts (2 errors) ==== + import { Date, Event } from './types'; + ~~~~ +!!! error TS2866: Import 'Date' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. + ~~~~~ +!!! error TS2866: Import 'Event' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. + function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); + } + function bar() { + return new Event('bar') as Event.T; + } + +==== ./good.ts (0 errors) ==== + import type { Date, Event } from './types'; + import { Console } from 'node:console'; + function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); + } + function bar() { + return new Event('bar') as Event.T; + } + const baz: Console = new Console(); + \ No newline at end of file diff --git a/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=true).errors.txt b/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=true).errors.txt new file mode 100644 index 0000000000000..deb401b76857d --- /dev/null +++ b/tests/baselines/reference/isolatedModulesShadowGlobalTypeNotValue(isolatedmodules=true,verbatimmodulesyntax=true).errors.txt @@ -0,0 +1,73 @@ +bad.ts(1,10): error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. +bad.ts(1,10): error TS1484: 'Date' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. +bad.ts(1,10): error TS2866: Import 'Date' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. +bad.ts(1,16): error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. +bad.ts(1,16): error TS1484: 'Event' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. +bad.ts(1,16): error TS2866: Import 'Event' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. +good.ts(2,10): error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + + +==== ./types.ts (0 errors) ==== + export interface Date { + day: number; + month: number; + year: number; + } + + export namespace Event { + export type T = any; + } + +==== ./node.d.ts (0 errors) ==== + declare module 'node:console' { + global { + interface Console { + Console: console.ConsoleConstructor; + } + namespace console { + interface ConsoleConstructor { + prototype: Console; + new (): Console; + } + } + var console: Console; + } + export = globalThis.console; + } + +==== ./bad.ts (6 errors) ==== + import { Date, Event } from './types'; + ~~~~ +!!! error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + ~~~~ +!!! error TS1484: 'Date' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. + ~~~~ +!!! error TS2866: Import 'Date' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. + ~~~~~ +!!! error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + ~~~~~ +!!! error TS1484: 'Event' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled. + ~~~~~ +!!! error TS2866: Import 'Event' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled. + function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); + } + function bar() { + return new Event('bar') as Event.T; + } + +==== ./good.ts (1 errors) ==== + import type { Date, Event } from './types'; + import { Console } from 'node:console'; + ~~~~~~~ +!!! error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled. + function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); + } + function bar() { + return new Event('bar') as Event.T; + } + const baz: Console = new Console(); + \ No newline at end of file diff --git a/tests/cases/compiler/isolatedModulesShadowGlobalTypeNotValue.ts b/tests/cases/compiler/isolatedModulesShadowGlobalTypeNotValue.ts new file mode 100644 index 0000000000000..9b0b7bd7c65e8 --- /dev/null +++ b/tests/cases/compiler/isolatedModulesShadowGlobalTypeNotValue.ts @@ -0,0 +1,54 @@ +// @isolatedModules: false, true +// @verbatimModuleSyntax: false, true +// @noEmit: true +// @noTypesAndSymbols: true + +// @filename: ./types.ts +export interface Date { + day: number; + month: number; + year: number; +} + +export namespace Event { + export type T = any; +} + +// @filename: ./node.d.ts +declare module 'node:console' { + global { + interface Console { + Console: console.ConsoleConstructor; + } + namespace console { + interface ConsoleConstructor { + prototype: Console; + new (): Console; + } + } + var console: Console; + } + export = globalThis.console; +} + +// @filename: ./bad.ts +import { Date, Event } from './types'; +function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); +} +function bar() { + return new Event('bar') as Event.T; +} + +// @filename: ./good.ts +import type { Date, Event } from './types'; +import { Console } from 'node:console'; +function foo(a: Date) { + const b = new Date(a.year, a.month, a.day); + return b.getTime(); +} +function bar() { + return new Event('bar') as Event.T; +} +const baz: Console = new Console();