diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2578ba4c4df..b688e9e1e52 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -40710,7 +40710,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment_or_type_query); } - if (getIsolatedModules(compilerOptions)) { + // --verbatimModuleSyntax only gets checked here when the enum usage does not + // resolve to an import, because imports of ambient const enums get checked + // separately in `checkAliasSymbol`. + if ( + compilerOptions.isolatedModules + || compilerOptions.verbatimModuleSyntax + && ok + && !resolveName( + node, + getFirstIdentifier(node as EntityNameOrEntityNameExpression), + SymbolFlags.Alias, + /*nameNotFoundMessage*/ undefined, + /*isUse*/ false, + /*excludeGlobals*/ true, + ) + ) { Debug.assert(!!(type.symbol.flags & SymbolFlags.ConstEnum)); const constEnumDeclaration = type.symbol.valueDeclaration as EnumDeclaration; const redirect = host.getRedirectReferenceForResolutionFromSourceOfProject(getSourceFileOfNode(constEnumDeclaration).resolvedPath); @@ -47264,6 +47279,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // in files that are unambiguously CommonJS in this mode. error(node, Diagnostics.ESM_syntax_is_not_allowed_in_a_CommonJS_module_when_module_is_set_to_preserve); } + + if ( + compilerOptions.verbatimModuleSyntax && + !isTypeOnlyImportOrExportDeclaration(node) && + !(node.flags & NodeFlags.Ambient) && + targetFlags & SymbolFlags.ConstEnum + ) { + const constEnumDeclaration = target.valueDeclaration as EnumDeclaration; + const redirect = host.getRedirectReferenceForResolutionFromSourceOfProject(getSourceFileOfNode(constEnumDeclaration).resolvedPath); + if (constEnumDeclaration.flags & NodeFlags.Ambient && (!redirect || !shouldPreserveConstEnums(redirect.commandLine.options))) { + error(node, Diagnostics.Cannot_access_ambient_const_enums_when_0_is_enabled, isolatedModulesLikeFlagName); + } + } } if (isImportSpecifier(node)) { diff --git a/src/testRunner/unittests/tsc/projectReferences.ts b/src/testRunner/unittests/tsc/projectReferences.ts index d1dda68e078..27a575ca02a 100644 --- a/src/testRunner/unittests/tsc/projectReferences.ts +++ b/src/testRunner/unittests/tsc/projectReferences.ts @@ -115,4 +115,42 @@ describe("unittests:: tsc:: projectReferences::", () => { }), commandLineArgs: ["--p", "src/project"], }); + + verifyTsc({ + scenario: "projectReferences", + subScenario: "importing const enum from referenced project with preserveConstEnums and verbatimModuleSyntax", + fs: () => + loadProjectFromFiles({ + "/src/preserve/index.ts": "export const enum E { A = 1 }", + "/src/preserve/index.d.ts": "export declare const enum E { A = 1 }", + "/src/preserve/tsconfig.json": jsonToReadableText({ + compilerOptions: { + composite: true, + declaration: true, + preserveConstEnums: true, + }, + }), + "/src/no-preserve/index.ts": "export const enum E { A = 1 }", + "/src/no-preserve/index.d.ts": "export declare const enum F { A = 1 }", + "/src/no-preserve/tsconfig.json": jsonToReadableText({ + compilerOptions: { + composite: true, + declaration: true, + preserveConstEnums: false, + }, + }), + "/src/project/index.ts": `import { E } from "../preserve";\nimport { F } from "../no-preserve";\nE.A; F.A;`, + "/src/project/tsconfig.json": jsonToReadableText({ + compilerOptions: { + module: "preserve", + verbatimModuleSyntax: true, + }, + references: [ + { path: "../preserve" }, + { path: "../no-preserve" }, + ], + }), + }), + commandLineArgs: ["--p", "src/project", "--pretty", "false"], + }); }); diff --git a/tests/baselines/reference/tsc/projectReferences/importing-const-enum-from-referenced-project-with-preserveConstEnums-and-verbatimModuleSyntax.js b/tests/baselines/reference/tsc/projectReferences/importing-const-enum-from-referenced-project-with-preserveConstEnums-and-verbatimModuleSyntax.js new file mode 100644 index 00000000000..45a6868c9ec --- /dev/null +++ b/tests/baselines/reference/tsc/projectReferences/importing-const-enum-from-referenced-project-with-preserveConstEnums-and-verbatimModuleSyntax.js @@ -0,0 +1,83 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Input:: +//// [/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +declare const console: { log(msg: any): void; }; + +//// [/src/no-preserve/index.d.ts] +export declare const enum F { A = 1 } + +//// [/src/no-preserve/index.ts] +export const enum E { A = 1 } + +//// [/src/no-preserve/tsconfig.json] +{ + "compilerOptions": { + "composite": true, + "declaration": true, + "preserveConstEnums": false + } +} + +//// [/src/preserve/index.d.ts] +export declare const enum E { A = 1 } + +//// [/src/preserve/index.ts] +export const enum E { A = 1 } + +//// [/src/preserve/tsconfig.json] +{ + "compilerOptions": { + "composite": true, + "declaration": true, + "preserveConstEnums": true + } +} + +//// [/src/project/index.ts] +import { E } from "../preserve"; +import { F } from "../no-preserve"; +E.A; F.A; + +//// [/src/project/tsconfig.json] +{ + "compilerOptions": { + "module": "preserve", + "verbatimModuleSyntax": true + }, + "references": [ + { + "path": "../preserve" + }, + { + "path": "../no-preserve" + } + ] +} + + + +Output:: +/lib/tsc --p src/project --pretty false +src/project/index.ts(2,10): error TS2748: Cannot access ambient const enums when 'verbatimModuleSyntax' is enabled. +exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated + + +//// [/src/project/index.js] +import { E } from "../preserve"; +import { F } from "../no-preserve"; +E.A; +F.A; + + diff --git a/tests/baselines/reference/verbatimModuleSyntaxAmbientConstEnum.errors.txt b/tests/baselines/reference/verbatimModuleSyntaxAmbientConstEnum.errors.txt new file mode 100644 index 00000000000..e405f0bed9c --- /dev/null +++ b/tests/baselines/reference/verbatimModuleSyntaxAmbientConstEnum.errors.txt @@ -0,0 +1,27 @@ +/a.ts(1,10): error TS2748: Cannot access ambient const enums when 'verbatimModuleSyntax' is enabled. +/a.ts(4,1): error TS2748: Cannot access ambient const enums when 'verbatimModuleSyntax' is enabled. +/b.ts(1,10): error TS2748: Cannot access ambient const enums when 'verbatimModuleSyntax' is enabled. + + +==== /node_modules/pkg/index.d.ts (0 errors) ==== + export declare const enum E { A, B, C } + declare global { + const enum F { A, B, C } + } + +==== /a.ts (2 errors) ==== + import { E } from "pkg"; // Error + ~ +!!! error TS2748: Cannot access ambient const enums when 'verbatimModuleSyntax' is enabled. + import type { E as _E } from "pkg"; // Ok + console.log(E.A); // Ok + F.A; // Error + ~ +!!! error TS2748: Cannot access ambient const enums when 'verbatimModuleSyntax' is enabled. + +==== /b.ts (1 errors) ==== + export { E } from "pkg"; // Error + ~ +!!! error TS2748: Cannot access ambient const enums when 'verbatimModuleSyntax' is enabled. + export type { E as _E } from "pkg"; // Ok + \ No newline at end of file diff --git a/tests/cases/conformance/externalModules/verbatimModuleSyntaxAmbientConstEnum.ts b/tests/cases/conformance/externalModules/verbatimModuleSyntaxAmbientConstEnum.ts new file mode 100644 index 00000000000..d59dbadf5d6 --- /dev/null +++ b/tests/cases/conformance/externalModules/verbatimModuleSyntaxAmbientConstEnum.ts @@ -0,0 +1,21 @@ +// @verbatimModuleSyntax: true +// @target: esnext +// @module: preserve +// @noEmit: true +// @noTypesAndSymbols: true + +// @Filename: /node_modules/pkg/index.d.ts +export declare const enum E { A, B, C } +declare global { + const enum F { A, B, C } +} + +// @Filename: /a.ts +import { E } from "pkg"; // Error +import type { E as _E } from "pkg"; // Ok +console.log(E.A); // Ok +F.A; // Error + +// @Filename: /b.ts +export { E } from "pkg"; // Error +export type { E as _E } from "pkg"; // Ok