diff --git a/internal/checker/emitresolver.go b/internal/checker/emitresolver.go index ba40b72a08..df7011e5c4 100644 --- a/internal/checker/emitresolver.go +++ b/internal/checker/emitresolver.go @@ -498,6 +498,30 @@ func (r *emitResolver) IsImportRequiredByAugmentation(decl *ast.ImportDeclaratio return false } +func (r *emitResolver) IsDefinitelyReferenceToGlobalSymbolObject(node *ast.Node) bool { + if !ast.IsPropertyAccessExpression(node) || + !ast.IsIdentifier(node.Name()) || + !ast.IsPropertyAccessExpression(node.Expression()) && !ast.IsIdentifier(node.Expression()) { + return false + } + if node.Expression().Kind == ast.KindIdentifier { + if node.Expression().AsIdentifier().Text != "Symbol" { + return false + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + // Exactly `Symbol.something` and `Symbol` either does not resolve or definitely resolves to the global Symbol + return r.checker.getResolvedSymbol(node.Expression()) == r.checker.getGlobalSymbol("Symbol", ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*diagnostic*/) + } + if node.Expression().Expression().Kind != ast.KindIdentifier || node.Expression().Expression().AsIdentifier().Text != "globalThis" || node.Expression().Name().Text() != "Symbol" { + return false + } + r.checkerMu.Lock() + defer r.checkerMu.Unlock() + // Exactly `globalThis.Symbol.something` and `globalThis` resolves to the global `globalThis` + return r.checker.getResolvedSymbol(node.Expression().Expression()) == r.checker.globalThisSymbol +} + func (r *emitResolver) RequiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { if !ast.IsParseTreeNode(declaration) { return false diff --git a/internal/printer/emitresolver.go b/internal/printer/emitresolver.go index 6330d2e737..156709cf68 100644 --- a/internal/printer/emitresolver.go +++ b/internal/printer/emitresolver.go @@ -52,6 +52,7 @@ type EmitResolver interface { RequiresAddingImplicitUndefined(node *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool IsDeclarationVisible(node *ast.Node) bool IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool + IsDefinitelyReferenceToGlobalSymbolObject(node *ast.Node) bool IsImplementationOfOverload(node *ast.SignatureDeclaration) bool GetEnumMemberValue(node *ast.Node) evaluator.Result IsLateBound(node *ast.Node) bool diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index 0702913b00..07ff921521 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -384,8 +384,20 @@ func (tx *DeclarationTransformer) visitDeclarationSubtree(input *ast.Node) *ast. } if ast.HasDynamicName(input) { if tx.state.isolatedDeclarations { - // !!! isolatedDeclarations support - return nil + // Classes and object literals usually elide properties with computed names that are not of a literal type + // In isolated declarations TSC needs to error on these as we don't know the type in a DTE. + if !tx.resolver.IsDefinitelyReferenceToGlobalSymbolObject(input.Name().Expression()) { + if ast.IsClassDeclaration(input.Parent) || ast.IsObjectLiteralExpression(input.Parent) { + // !!! TODO: isolatedDeclarations diagnostics + // context.addDiagnostic(createDiagnosticForNode(input, diagnostics.Computed_property_names_on_class_or_object_literals_cannot_be_inferred_with_isolatedDeclarations)) + return nil + } else if (ast.IsInterfaceDeclaration(input.Parent) || ast.IsTypeLiteralNode(input.Parent)) && !ast.IsEntityNameExpression(input.Name().Expression()) { + // Type declarations just need to double-check that the input computed name is an entity name expression + // !!! TODO: isolatedDeclarations diagnostics + // context.addDiagnostic(createDiagnosticForNode(input, diagnostics.Computed_properties_must_be_number_or_string_literals_variables_or_dotted_expressions_with_isolatedDeclarations)) + return nil + } + } } else if !tx.resolver.IsLateBound(tx.EmitContext().ParseNode(input)) || !ast.IsEntityNameExpression(input.Name().AsComputedPropertyName().Expression) { return nil } diff --git a/testdata/baselines/reference/compiler/enumMemberInterfacePropertyDeclarationEmit.js b/testdata/baselines/reference/compiler/enumMemberInterfacePropertyDeclarationEmit.js new file mode 100644 index 0000000000..25de407399 --- /dev/null +++ b/testdata/baselines/reference/compiler/enumMemberInterfacePropertyDeclarationEmit.js @@ -0,0 +1,43 @@ +//// [tests/cases/compiler/enumMemberInterfacePropertyDeclarationEmit.ts] //// + +//// [enum.ts] +export enum WWMF{ + AAR = 'AAR', +} + +//// [base.ts] +import type { WWMF } from "./enum"; + +interface WWMFMap { + [WWMF.AAR]?: any; +} + +export const wwmfMap: WWMFMap = {}; + + +//// [enum.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WWMF = void 0; +var WWMF; +(function (WWMF) { + WWMF["AAR"] = "AAR"; +})(WWMF || (exports.WWMF = WWMF = {})); +//// [base.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.wwmfMap = void 0; +exports.wwmfMap = {}; + + +//// [enum.d.ts] +export declare enum WWMF { + AAR = "AAR" +} +//// [base.d.ts] +import type { WWMF } from "./enum"; +interface WWMFMap { + [WWMF.AAR]?: any; +} +export declare const wwmfMap: WWMFMap; +export {}; diff --git a/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js b/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js index 6b9d7cbeff..5b6872ec8b 100644 --- a/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js +++ b/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js @@ -112,9 +112,14 @@ export declare class Cls { get getSetOk3(): number; set getSetOk3(value: number); } +declare let noAnnotationStringName: string; +declare const noAnnotationLiteralName = "noAnnotationLiteralName"; export declare class C { [x: string]: any; [x: number]: number; } export interface I { + [noAnnotationStringName]: 10; + [noAnnotationLiteralName](): any; } +export {}; diff --git a/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js.diff b/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js.diff index b20dba8c61..8c37d0e27a 100644 --- a/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js.diff +++ b/testdata/baselines/reference/submodule/compiler/isolatedDeclarationErrorsClasses.js.diff @@ -24,9 +24,14 @@ + get getSetOk3(): number; + set getSetOk3(value: number); +} ++declare let noAnnotationStringName: string; ++declare const noAnnotationLiteralName = "noAnnotationLiteralName"; +export declare class C { + [x: string]: any; + [x: number]: number; +} +export interface I { -+} \ No newline at end of file ++ [noAnnotationStringName]: 10; ++ [noAnnotationLiteralName](): any; ++} ++export {}; \ No newline at end of file diff --git a/testdata/tests/cases/compiler/enumMemberInterfacePropertyDeclarationEmit.ts b/testdata/tests/cases/compiler/enumMemberInterfacePropertyDeclarationEmit.ts new file mode 100644 index 0000000000..8c7a4c143b --- /dev/null +++ b/testdata/tests/cases/compiler/enumMemberInterfacePropertyDeclarationEmit.ts @@ -0,0 +1,17 @@ +// @declaration: true +// @noTypesAndSymbols: true +// @isolatedDeclarations: true + +// @Filename: enum.ts +export enum WWMF{ + AAR = 'AAR', +} + +// @Filename: base.ts +import type { WWMF } from "./enum"; + +interface WWMFMap { + [WWMF.AAR]?: any; +} + +export const wwmfMap: WWMFMap = {};