diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 75cf8829b2c21..874103578d2b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7777,7 +7777,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { context.tracker.reportNonSerializableProperty(symbolToString(propertySymbol)); } } - context.enclosingDeclaration = propertySymbol.valueDeclaration || propertySymbol.declarations?.[0] || saveEnclosingDeclaration; + // For unique symbol property keys from external modules, preserve the original enclosing declaration + // to enable proper symbol accessibility checking and import tracking. + // Built-in symbols (like Symbol.iterator) should use the normal enclosing declaration handling. + const propNameType = getSymbolLinks(propertySymbol).nameType; + let useOriginalEnclosing = false; + if (propNameType && (propNameType.flags & TypeFlags.UniqueESSymbol) && saveEnclosingDeclaration) { + const symDecl = (propNameType as UniqueESSymbolType).symbol.declarations; + const symbolSourceFile = symDecl && symDecl[0] && getSourceFileOfNode(symDecl[0]); + const enclosingSourceFile = getSourceFileOfNode(saveEnclosingDeclaration); + // Only use original enclosing for symbols from different files (external modules) + useOriginalEnclosing = !!(symbolSourceFile && enclosingSourceFile && symbolSourceFile !== enclosingSourceFile); + } + context.enclosingDeclaration = useOriginalEnclosing + ? saveEnclosingDeclaration + : (propertySymbol.valueDeclaration || propertySymbol.declarations?.[0] || saveEnclosingDeclaration); const propertyName = getPropertyNameNodeForSymbol(propertySymbol, context); context.enclosingDeclaration = saveEnclosingDeclaration; context.approximateLength += symbolName(propertySymbol).length + 1; @@ -9015,7 +9029,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createPropertyNameNodeForIdentifierOrLiteral(name, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed, isMethod); } if (nameType.flags & TypeFlags.UniqueESSymbol) { - return factory.createComputedPropertyName(symbolToExpression((nameType as UniqueESSymbolType).symbol, context, SymbolFlags.Value)); + const uniqueSymbol = (nameType as UniqueESSymbolType).symbol; + // For unique symbol property keys from external modules, verify value accessibility + // and report error if the symbol cannot be named without an import + if (context.tracker.canTrackSymbol && context.enclosingDeclaration && uniqueSymbol.declarations?.length) { + const symSourceFile = getSourceFileOfNode(uniqueSymbol.declarations[0]); + const enclosingFile = getSourceFileOfNode(context.enclosingDeclaration); + // Only check for symbols from different source files (external modules) + if (symSourceFile && enclosingFile && symSourceFile !== enclosingFile) { + // Check if symbol is directly accessible as a value (not via parent module) + if (!isSymbolAccessibleByFlags(uniqueSymbol, context.enclosingDeclaration, SymbolFlags.Value)) { + if (context.tracker.reportNonSerializableProperty) { + context.tracker.reportNonSerializableProperty(symbolToString(uniqueSymbol)); + } + } + } + } + return factory.createComputedPropertyName(symbolToExpression(uniqueSymbol, context, SymbolFlags.Value)); } } } diff --git a/tests/baselines/reference/declarationEmitSymbolPropertyKey.errors.txt b/tests/baselines/reference/declarationEmitSymbolPropertyKey.errors.txt new file mode 100644 index 0000000000000..97be0292d90e3 --- /dev/null +++ b/tests/baselines/reference/declarationEmitSymbolPropertyKey.errors.txt @@ -0,0 +1,24 @@ +index.ts(2,14): error TS4118: The type of this node cannot be serialized because its property 'lostSymbol' cannot be serialized. + + +==== node_modules/test-pkg/index.d.ts (0 errors) ==== + declare const lostSymbol: unique symbol; + type lostSymbol = typeof lostSymbol; + + type SomeGeneric = { + [lostSymbol]: T; + }; + + declare function fn(): SomeGeneric; + + export { + lostSymbol, + fn + }; + +==== index.ts (1 errors) ==== + import { fn } from 'test-pkg'; + export const value = fn(); + ~~~~~ +!!! error TS4118: The type of this node cannot be serialized because its property 'lostSymbol' cannot be serialized. + \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmitSymbolPropertyKey.js b/tests/baselines/reference/declarationEmitSymbolPropertyKey.js new file mode 100644 index 0000000000000..cfe51e183c9a6 --- /dev/null +++ b/tests/baselines/reference/declarationEmitSymbolPropertyKey.js @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/declarationEmitSymbolPropertyKey.ts] //// + +//// [index.d.ts] +declare const lostSymbol: unique symbol; +type lostSymbol = typeof lostSymbol; + +type SomeGeneric = { + [lostSymbol]: T; +}; + +declare function fn(): SomeGeneric; + +export { + lostSymbol, + fn +}; + +//// [index.ts] +import { fn } from 'test-pkg'; +export const value = fn(); + + +//// [index.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.value = void 0; +const test_pkg_1 = require("test-pkg"); +exports.value = (0, test_pkg_1.fn)(); diff --git a/tests/baselines/reference/declarationEmitSymbolPropertyKey.symbols b/tests/baselines/reference/declarationEmitSymbolPropertyKey.symbols new file mode 100644 index 0000000000000..c66b0c914c17b --- /dev/null +++ b/tests/baselines/reference/declarationEmitSymbolPropertyKey.symbols @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/declarationEmitSymbolPropertyKey.ts] //// + +=== node_modules/test-pkg/index.d.ts === +declare const lostSymbol: unique symbol; +>lostSymbol : Symbol(lostSymbol, Decl(index.d.ts, 0, 13), Decl(index.d.ts, 0, 40)) + +type lostSymbol = typeof lostSymbol; +>lostSymbol : Symbol(lostSymbol, Decl(index.d.ts, 0, 13), Decl(index.d.ts, 0, 40)) +>lostSymbol : Symbol(lostSymbol, Decl(index.d.ts, 0, 13), Decl(index.d.ts, 0, 40)) + +type SomeGeneric = { +>SomeGeneric : Symbol(SomeGeneric, Decl(index.d.ts, 1, 36)) +>T : Symbol(T, Decl(index.d.ts, 3, 17)) + + [lostSymbol]: T; +>[lostSymbol] : Symbol([lostSymbol], Decl(index.d.ts, 3, 23)) +>lostSymbol : Symbol(lostSymbol, Decl(index.d.ts, 0, 13), Decl(index.d.ts, 0, 40)) +>T : Symbol(T, Decl(index.d.ts, 3, 17)) + +}; + +declare function fn(): SomeGeneric; +>fn : Symbol(fn, Decl(index.d.ts, 5, 2)) +>SomeGeneric : Symbol(SomeGeneric, Decl(index.d.ts, 1, 36)) + +export { + lostSymbol, +>lostSymbol : Symbol(lostSymbol, Decl(index.d.ts, 9, 8)) + + fn +>fn : Symbol(fn, Decl(index.d.ts, 10, 15)) + +}; + +=== index.ts === +import { fn } from 'test-pkg'; +>fn : Symbol(fn, Decl(index.ts, 0, 8)) + +export const value = fn(); +>value : Symbol(value, Decl(index.ts, 1, 12)) +>fn : Symbol(fn, Decl(index.ts, 0, 8)) + diff --git a/tests/baselines/reference/declarationEmitSymbolPropertyKey.types b/tests/baselines/reference/declarationEmitSymbolPropertyKey.types new file mode 100644 index 0000000000000..bf048c9f5a43d --- /dev/null +++ b/tests/baselines/reference/declarationEmitSymbolPropertyKey.types @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/declarationEmitSymbolPropertyKey.ts] //// + +=== node_modules/test-pkg/index.d.ts === +declare const lostSymbol: unique symbol; +>lostSymbol : unique symbol +> : ^^^^^^^^^^^^^ + +type lostSymbol = typeof lostSymbol; +>lostSymbol : unique symbol +> : ^^^^^^^^^^^^^ +>lostSymbol : unique symbol +> : ^^^^^^^^^^^^^ + +type SomeGeneric = { +>SomeGeneric : SomeGeneric +> : ^^^^^^^^^^^^^^ + + [lostSymbol]: T; +>[lostSymbol] : T +> : ^ +>lostSymbol : unique symbol +> : ^^^^^^^^^^^^^ + +}; + +declare function fn(): SomeGeneric; +>fn : () => SomeGeneric +> : ^^^^^^ + +export { + lostSymbol, +>lostSymbol : unique symbol +> : ^^^^^^^^^^^^^ + + fn +>fn : () => SomeGeneric +> : ^^^^^^ + +}; + +=== index.ts === +import { fn } from 'test-pkg'; +>fn : () => { [lostSymbol]: unknown; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +export const value = fn(); +>value : { [lostSymbol]: unknown; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn() : { [lostSymbol]: unknown; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn : () => { [lostSymbol]: unknown; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/cases/compiler/declarationEmitSymbolPropertyKey.ts b/tests/cases/compiler/declarationEmitSymbolPropertyKey.ts new file mode 100644 index 0000000000000..7e7d1047f072f --- /dev/null +++ b/tests/cases/compiler/declarationEmitSymbolPropertyKey.ts @@ -0,0 +1,22 @@ +// @target: es2015 +// @declaration: true +// @module: commonjs + +// @filename: node_modules/test-pkg/index.d.ts +declare const lostSymbol: unique symbol; +type lostSymbol = typeof lostSymbol; + +type SomeGeneric = { + [lostSymbol]: T; +}; + +declare function fn(): SomeGeneric; + +export { + lostSymbol, + fn +}; + +// @filename: index.ts +import { fn } from 'test-pkg'; +export const value = fn();