diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 47950566dfdaa..cec04589a2cc7 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -344,6 +344,9 @@ namespace ts { Debug.assert(isWellKnownSymbolSyntactically(nameExpression)); return getPropertyNameForKnownSymbolName(idText((nameExpression).name)); } + if (isWellKnownSymbolSyntactically(name)) { + return getPropertyNameForKnownSymbolName(idText(name.name)); + } return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined; } switch (node.kind) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d034b3544e646..78891d7dfe255 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -819,7 +819,14 @@ namespace ts { export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName; - export type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | ElementAccessExpression | BindingPattern; + export type DeclarationName = + | Identifier + | StringLiteralLike + | NumericLiteral + | ComputedPropertyName + | ElementAccessExpression + | BindingPattern + | EntityNameExpression; export interface Declaration extends Node { _declarationBrand: any; @@ -1884,13 +1891,17 @@ namespace ts { | CallChainRoot ; + /** @internal */ + export interface WellKnownSymbolExpression extends PropertyAccessExpression { + expression: Identifier & { escapedText: "Symbol" }; + } /** @internal */ export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: BindableStaticNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } }; /** @internal */ export type BindableStaticNameExpression = EntityNameExpression | BindableStaticElementAccessExpression; /** @internal */ export type LiteralLikeElementAccessExpression = ElementAccessExpression & Declaration & { - argumentExpression: StringLiteralLike | NumericLiteral; + argumentExpression: StringLiteralLike | NumericLiteral | WellKnownSymbolExpression; }; /** @internal */ export type BindableStaticElementAccessExpression = LiteralLikeElementAccessExpression & { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a0988cfa7e319..9549d313df6b9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2068,7 +2068,9 @@ namespace ts { } export function isLiteralLikeElementAccess(node: Node): node is LiteralLikeElementAccessExpression { - return isElementAccessExpression(node) && isStringOrNumericLiteralLike(node.argumentExpression); + return isElementAccessExpression(node) && ( + isStringOrNumericLiteralLike(node.argumentExpression) || + isWellKnownSymbolSyntactically(node.argumentExpression)); } export function isBindableStaticAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticAccessExpression { @@ -2901,7 +2903,7 @@ namespace ts { * Symbol.name * where Symbol is literally the word "Symbol", and name is any identifierName */ - export function isWellKnownSymbolSyntactically(node: Expression): boolean { + export function isWellKnownSymbolSyntactically(node: Node): node is WellKnownSymbolExpression { return isPropertyAccessExpression(node) && isESSymbolIdentifier(node.expression); } diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 6de1de99cd759..03b9d6216c874 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -136,7 +136,7 @@ namespace ts.NavigationBar { for (let i = 0; i < depth; i++) endNode(); } function startNestedNodes(targetNode: Node, entityName: BindableStaticNameExpression) { - const names: PropertyNameLiteral[] = []; + const names: (PropertyNameLiteral | WellKnownSymbolExpression)[] = []; while (!isPropertyNameLiteral(entityName)) { const name = getNameOrArgument(entityName); const nameText = getElementOrPropertyAccessName(entityName); @@ -334,7 +334,7 @@ namespace ts.NavigationBar { assignmentTarget; let depth = 0; - let className: PropertyNameLiteral; + let className: PropertyNameLiteral | WellKnownSymbolExpression; // If we see a prototype assignment, start tracking the target as a class // This is only done for simple classes not nested assignments. if (isIdentifier(prototypeAccess.expression)) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 3616e96d9d812..d465c84dcc64d 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -548,7 +548,7 @@ declare namespace ts { } export type EntityName = Identifier | QualifiedName; export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName; - export type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | ElementAccessExpression | BindingPattern; + export type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | ElementAccessExpression | BindingPattern | EntityNameExpression; export interface Declaration extends Node { _declarationBrand: any; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8027692181810..21688456bacb8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -548,7 +548,7 @@ declare namespace ts { } export type EntityName = Identifier | QualifiedName; export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName; - export type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | ElementAccessExpression | BindingPattern; + export type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | ElementAccessExpression | BindingPattern | EntityNameExpression; export interface Declaration extends Node { _declarationBrand: any; } diff --git a/tests/baselines/reference/wellKnownSymbolExpando.symbols b/tests/baselines/reference/wellKnownSymbolExpando.symbols new file mode 100644 index 0000000000000..b343b2047f643 --- /dev/null +++ b/tests/baselines/reference/wellKnownSymbolExpando.symbols @@ -0,0 +1,10 @@ +=== tests/cases/compiler/wellKnownSymbolExpando.ts === +function f() {} +>f : Symbol(f, Decl(wellKnownSymbolExpando.ts, 0, 0), Decl(wellKnownSymbolExpando.ts, 0, 15)) + +f[Symbol.iterator] = function() {} +>f : Symbol(f, Decl(wellKnownSymbolExpando.ts, 0, 0), Decl(wellKnownSymbolExpando.ts, 0, 15)) +>Symbol.iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) +>iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) + diff --git a/tests/baselines/reference/wellKnownSymbolExpando.types b/tests/baselines/reference/wellKnownSymbolExpando.types new file mode 100644 index 0000000000000..f0a1c32a7f97c --- /dev/null +++ b/tests/baselines/reference/wellKnownSymbolExpando.types @@ -0,0 +1,13 @@ +=== tests/cases/compiler/wellKnownSymbolExpando.ts === +function f() {} +>f : typeof f + +f[Symbol.iterator] = function() {} +>f[Symbol.iterator] = function() {} : () => void +>f[Symbol.iterator] : () => void +>f : typeof f +>Symbol.iterator : symbol +>Symbol : SymbolConstructor +>iterator : symbol +>function() {} : () => void + diff --git a/tests/cases/compiler/wellKnownSymbolExpando.ts b/tests/cases/compiler/wellKnownSymbolExpando.ts new file mode 100644 index 0000000000000..b80fa2bb8b28d --- /dev/null +++ b/tests/cases/compiler/wellKnownSymbolExpando.ts @@ -0,0 +1,5 @@ +// @noEmit: true +// @target: esnext + +function f() {} +f[Symbol.iterator] = function() {} diff --git a/tests/cases/fourslash/navigationBarWellKnownSymbolExpando.ts b/tests/cases/fourslash/navigationBarWellKnownSymbolExpando.ts new file mode 100644 index 0000000000000..a6b9cadc1242f --- /dev/null +++ b/tests/cases/fourslash/navigationBarWellKnownSymbolExpando.ts @@ -0,0 +1,53 @@ +/// + +////function f() {} +////f[Symbol.iterator] = function() {} + +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "f", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "[Symbol.iterator]", + "kind": "function" + } + ] + } + ] +}); + +verify.navigationBar([ + { + "text": "", + "kind": "script", + "childItems": [ + { + "text": "f", + "kind": "class" + } + ] + }, + { + "text": "f", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "[Symbol.iterator]", + "kind": "function" + } + ], + "indent": 1 + } +]);