From 924890a8392a8180b497a20909f338efa6968aa5 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Aug 2018 09:53:03 -0700 Subject: [PATCH 1/2] Treat NoSubstitutionTemplateLiteral like StringLiteral in more places --- src/compiler/binder.ts | 2 +- src/compiler/checker.ts | 4 +- src/compiler/parser.ts | 2 +- src/compiler/transformers/destructuring.ts | 2 +- src/compiler/utilities.ts | 55 ++++++++++--------- src/services/navigationBar.ts | 2 +- src/services/rename.ts | 2 +- src/services/services.ts | 4 +- src/services/utilities.ts | 2 +- .../computedPropertyNames11_ES5.types | 4 +- .../computedPropertyNames11_ES6.types | 4 +- .../computedPropertyNames12_ES5.errors.txt | 5 +- .../computedPropertyNames12_ES6.errors.txt | 5 +- ...erPropertiesInES5ShouldBeTransformed.types | 4 +- 14 files changed, 48 insertions(+), 49 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 927b31403e074..5f5145d78243a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -259,7 +259,7 @@ namespace ts { if (name.kind === SyntaxKind.ComputedPropertyName) { const nameExpression = name.expression; // treat computed property names where expression is string/numeric literal as just string/numeric literal - if (isStringOrNumericLiteral(nameExpression)) { + if (isStringOrNumericLiteralLike(nameExpression)) { return escapeLeadingUnderscores(nameExpression.text); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 46c7fda9105fb..f9f5117fec4c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4442,7 +4442,7 @@ namespace ts { } function isComputedNonLiteralName(name: PropertyName): boolean { - return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteral(name.expression); + return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteralLike(name.expression); } function getRestType(source: Type, properties: PropertyName[], symbol: Symbol | undefined): Type { @@ -13715,7 +13715,7 @@ namespace ts { case SyntaxKind.Identifier: return idText(name); case SyntaxKind.ComputedPropertyName: - return isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined; + return isStringOrNumericLiteralLike(name.expression) ? name.expression.text : undefined; case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: return name.text; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 570d50f848532..94bbb358f7b2c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4465,7 +4465,7 @@ namespace ts { } else { const argument = allowInAnd(parseExpression); - if (isStringOrNumericLiteral(argument)) { + if (isStringOrNumericLiteralLike(argument)) { argument.text = internIdentifier(argument.text); } indexedAccess.argumentExpression = argument; diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 5f54e367d3e88..14d1659dc7626 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -447,7 +447,7 @@ namespace ts { const argumentExpression = ensureIdentifier(flattenContext, visitNode(propertyName.expression, flattenContext.visitor), /*reuseIdentifierExpressions*/ false, /*location*/ propertyName); return createElementAccess(value, argumentExpression); } - else if (isStringOrNumericLiteral(propertyName)) { + else if (isStringOrNumericLiteralLike(propertyName)) { const argumentExpression = getSynthesizedClone(propertyName); argumentExpression.text = argumentExpression.text; return createElementAccess(value, argumentExpression); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b285a9878486e..21651c49a49f9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -767,9 +767,9 @@ namespace ts { case SyntaxKind.NumericLiteral: return escapeLeadingUnderscores(name.text); case SyntaxKind.ComputedPropertyName: - return isStringOrNumericLiteral(name.expression) ? escapeLeadingUnderscores(name.expression.text) : undefined!; // TODO: GH#18217 Almost all uses of this assume the result to be defined! + return isStringOrNumericLiteralLike(name.expression) ? escapeLeadingUnderscores(name.expression.text) : undefined!; // TODO: GH#18217 Almost all uses of this assume the result to be defined! default: - Debug.assertNever(name); + return Debug.assertNever(name); } } @@ -2566,6 +2566,10 @@ namespace ts { || kind === SyntaxKind.NumericLiteral; } + export function isStringOrNumericLiteralLike(node: Node): node is StringLiteralLike | NumericLiteral { + return isStringOrNumericLiteral(node) || isNoSubstitutionTemplateLiteral(node); + } + /** * A declaration has a dynamic name if both of the following are true: * 1. The declaration has a computed property name @@ -2580,7 +2584,7 @@ namespace ts { export function isDynamicName(name: DeclarationName): boolean { return name.kind === SyntaxKind.ComputedPropertyName && - !isStringOrNumericLiteral(name.expression) && + !isStringOrNumericLiteralLike(name.expression) && !isWellKnownSymbolSyntactically(name.expression); } @@ -2593,24 +2597,25 @@ namespace ts { return isPropertyAccessExpression(node) && isESSymbolIdentifier(node.expression); } - export function getPropertyNameForPropertyNameNode(name: DeclarationName): __String | undefined { - if (name.kind === SyntaxKind.Identifier) { - return name.escapedText; - } - if (name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral) { - return escapeLeadingUnderscores(name.text); - } - if (name.kind === SyntaxKind.ComputedPropertyName) { - const nameExpression = name.expression; - if (isWellKnownSymbolSyntactically(nameExpression)) { - return getPropertyNameForKnownSymbolName(idText((nameExpression).name)); - } - else if (nameExpression.kind === SyntaxKind.StringLiteral || nameExpression.kind === SyntaxKind.NumericLiteral) { - return escapeLeadingUnderscores((nameExpression).text); - } + export function getPropertyNameForPropertyNameNode(name: PropertyName): __String | undefined { + switch (name.kind) { + case SyntaxKind.Identifier: + return name.escapedText; + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + return escapeLeadingUnderscores(name.text); + case SyntaxKind.ComputedPropertyName: + const nameExpression = name.expression; + if (isWellKnownSymbolSyntactically(nameExpression)) { + return getPropertyNameForKnownSymbolName(idText((nameExpression).name)); + } + else if (isStringOrNumericLiteralLike(nameExpression)) { + return escapeLeadingUnderscores(nameExpression.text); + } + return undefined; + default: + return Debug.assertNever(name); } - - return undefined; } export type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral; @@ -3325,17 +3330,17 @@ namespace ts { } } else { - forEach(declarations, (member: Declaration) => { - if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) + forEach(declarations, member => { + if (isAccessor(member) && hasModifier(member, ModifierFlags.Static) === hasModifier(accessor, ModifierFlags.Static)) { - const memberName = getPropertyNameForPropertyNameNode((member as NamedDeclaration).name!); + const memberName = getPropertyNameForPropertyNameNode(member.name); const accessorName = getPropertyNameForPropertyNameNode(accessor.name); if (memberName === accessorName) { if (!firstAccessor) { - firstAccessor = member; + firstAccessor = member; } else if (!secondAccessor) { - secondAccessor = member; + secondAccessor = member; } if (member.kind === SyntaxKind.GetAccessor && !getAccessor) { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index f23fa4c3493bc..504311a4a2db9 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -416,7 +416,7 @@ namespace ts.NavigationBar { } const declName = getNameOfDeclaration(node); - if (declName) { + if (declName && isPropertyName(declName)) { return unescapeLeadingUnderscores(getPropertyNameForPropertyNameNode(declName)!); // TODO: GH#18217 } switch (node.kind) { diff --git a/src/services/rename.ts b/src/services/rename.ts index 1f498d9771653..43f76ff4428ff 100644 --- a/src/services/rename.ts +++ b/src/services/rename.ts @@ -29,7 +29,7 @@ namespace ts.Rename { if (isStringLiteralLike(node) && tryGetImportFromModuleSpecifier(node)) return undefined; const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node); - const specifierName = (isImportOrExportSpecifierName(node) || isStringOrNumericLiteral(node) && node.parent.kind === SyntaxKind.ComputedPropertyName) + const specifierName = (isImportOrExportSpecifierName(node) || isStringOrNumericLiteralLike(node) && node.parent.kind === SyntaxKind.ComputedPropertyName) ? stripQuotes(getTextOfIdentifierOrLiteral(node)) : undefined; const displayName = specifierName || typeChecker.symbolToString(symbol); diff --git a/src/services/services.ts b/src/services/services.ts index 31349157ecf24..dcc321b00d58c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2142,7 +2142,7 @@ namespace ts { function initializeNameTable(sourceFile: SourceFile): void { const nameTable = sourceFile.nameTable = createUnderscoreEscapedMap(); sourceFile.forEachChild(function walk(node) { - if (isIdentifier(node) && node.escapedText || isStringOrNumericLiteral(node) && literalIsName(node)) { + if (isIdentifier(node) && node.escapedText || isStringOrNumericLiteralLike(node) && literalIsName(node)) { const text = getEscapedTextOfIdentifierOrLiteral(node); nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1); } @@ -2162,7 +2162,7 @@ namespace ts { * then we want 'something' to be in the name table. Similarly, if we have * "a['propname']" then we want to store "propname" in the name table. */ - function literalIsName(node: StringLiteral | NumericLiteral): boolean { + function literalIsName(node: StringLiteralLike | NumericLiteral): boolean { return isDeclarationName(node) || node.parent.kind === SyntaxKind.ExternalModuleReference || isArgumentOfElementAccessExpression(node) || diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e8d9c8a904793..b856f3112759e 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1240,7 +1240,7 @@ namespace ts { export function getNameFromPropertyName(name: PropertyName): string | undefined { return name.kind === SyntaxKind.ComputedPropertyName // treat computed property names where expression is string/numeric literal as just string/numeric literal - ? isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined + ? isStringOrNumericLiteralLike(name.expression) ? name.expression.text : undefined : getTextOfIdentifierOrLiteral(name); } diff --git a/tests/baselines/reference/computedPropertyNames11_ES5.types b/tests/baselines/reference/computedPropertyNames11_ES5.types index 86db4281f459b..b5ef2e02b2624 100644 --- a/tests/baselines/reference/computedPropertyNames11_ES5.types +++ b/tests/baselines/reference/computedPropertyNames11_ES5.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } ->{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } +>v : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; [`hello bye`]: any; } +>{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; [`hello bye`]: any; } get [s]() { return 0; }, >[s] : number diff --git a/tests/baselines/reference/computedPropertyNames11_ES6.types b/tests/baselines/reference/computedPropertyNames11_ES6.types index cc877e9b64538..0cec215f33c04 100644 --- a/tests/baselines/reference/computedPropertyNames11_ES6.types +++ b/tests/baselines/reference/computedPropertyNames11_ES6.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } ->{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } +>v : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; [`hello bye`]: any; } +>{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; [`hello bye`]: any; } get [s]() { return 0; }, >[s] : number diff --git a/tests/baselines/reference/computedPropertyNames12_ES5.errors.txt b/tests/baselines/reference/computedPropertyNames12_ES5.errors.txt index 309b734dfeaf7..62c161f231ff7 100644 --- a/tests/baselines/reference/computedPropertyNames12_ES5.errors.txt +++ b/tests/baselines/reference/computedPropertyNames12_ES5.errors.txt @@ -5,11 +5,10 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts(8, tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts(9,5): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts(12,5): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts(13,12): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. -tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts(14,5): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts(15,12): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. -==== tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts (9 errors) ==== +==== tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts (8 errors) ==== var s: string; var n: number; var a: any; @@ -38,8 +37,6 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES5.ts(15 ~~~~~~~~~~~ !!! error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. [`hello bye`] = 0; - ~~~~~~~~~~~~~ -!!! error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. static [`hello ${a} bye`] = 0 ~~~~~~~~~~~~~~~~~~ !!! error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. diff --git a/tests/baselines/reference/computedPropertyNames12_ES6.errors.txt b/tests/baselines/reference/computedPropertyNames12_ES6.errors.txt index 13920995eaa9a..75788c857a45c 100644 --- a/tests/baselines/reference/computedPropertyNames12_ES6.errors.txt +++ b/tests/baselines/reference/computedPropertyNames12_ES6.errors.txt @@ -5,11 +5,10 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts(8, tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts(9,5): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts(12,5): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts(13,12): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. -tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts(14,5): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts(15,12): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. -==== tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts (9 errors) ==== +==== tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts (8 errors) ==== var s: string; var n: number; var a: any; @@ -38,8 +37,6 @@ tests/cases/conformance/es6/computedProperties/computedPropertyNames12_ES6.ts(15 ~~~~~~~~~~~ !!! error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. [`hello bye`] = 0; - ~~~~~~~~~~~~~ -!!! error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. static [`hello ${a} bye`] = 0 ~~~~~~~~~~~~~~~~~~ !!! error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type. diff --git a/tests/baselines/reference/computerPropertiesInES5ShouldBeTransformed.types b/tests/baselines/reference/computerPropertiesInES5ShouldBeTransformed.types index b0e4e1424acce..8e1d05a159cb8 100644 --- a/tests/baselines/reference/computerPropertiesInES5ShouldBeTransformed.types +++ b/tests/baselines/reference/computerPropertiesInES5ShouldBeTransformed.types @@ -1,7 +1,7 @@ === tests/cases/compiler/computerPropertiesInES5ShouldBeTransformed.ts === const b = ({ [`key`]: renamed }) => renamed; ->b : ({ [`key`]: renamed }: {}) => any ->({ [`key`]: renamed }) => renamed : ({ [`key`]: renamed }: {}) => any +>b : ({ [`key`]: renamed }: { key: any; }) => any +>({ [`key`]: renamed }) => renamed : ({ [`key`]: renamed }: { key: any; }) => any >`key` : "key" >renamed : any >renamed : any From 40972baa412f4be49dfb3ae09d2b39b514de1e20 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Aug 2018 15:28:59 -0700 Subject: [PATCH 2/2] Move isStringOrNumericLiteral closer to its only use --- src/compiler/factory.ts | 8 +++++++- src/compiler/utilities.ts | 8 +------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 226da57dbbe14..4bb374682e087 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -4709,7 +4709,7 @@ namespace ts { /** * Gets the property name of a BindingOrAssignmentElement */ - export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement) { + export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): PropertyName | undefined { switch (bindingElement.kind) { case SyntaxKind.BindingElement: // `a` in `let { a: b } = ...` @@ -4754,6 +4754,12 @@ namespace ts { Debug.fail("Invalid property name for binding element."); } + function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral { + const kind = node.kind; + return kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.NumericLiteral; + } + /** * Gets the elements of a BindingOrAssignmentPattern */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 21651c49a49f9..aa0d432a5f146 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2560,14 +2560,8 @@ namespace ts { return false; } - export function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral { - const kind = node.kind; - return kind === SyntaxKind.StringLiteral - || kind === SyntaxKind.NumericLiteral; - } - export function isStringOrNumericLiteralLike(node: Node): node is StringLiteralLike | NumericLiteral { - return isStringOrNumericLiteral(node) || isNoSubstitutionTemplateLiteral(node); + return isStringLiteralLike(node) || isNumericLiteral(node); } /**