mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Better template literals support in checker (#32064)
* Support template literals in enum declarations * Support template literals in const enum access * Support template literals in swith with typeof narrowing * Support template literals in element access discriminant * Support template literals in ambient module declaration * Unify symbols for template literals in computed properties * Unify expression position checks for template literals * Support template literals in rename and find all references * Mark computed properties with template literals as write access * Inline startsWithQuote
This commit is contained in:
committed by
Nathan Shively-Sanders
parent
c0573c59c9
commit
a34fdb203e
@@ -850,7 +850,7 @@ namespace ts {
|
||||
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
|
||||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
|
||||
isElementAccessExpression(expr) &&
|
||||
(isStringLiteral(expr.argumentExpression) || isNumericLiteral(expr.argumentExpression)) &&
|
||||
isStringOrNumericLiteralLike(expr.argumentExpression) &&
|
||||
isNarrowableReference(expr.expression);
|
||||
}
|
||||
|
||||
|
||||
+11
-9
@@ -7930,7 +7930,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isStringConcatExpression(expr: Node): boolean {
|
||||
if (expr.kind === SyntaxKind.StringLiteral) {
|
||||
if (isStringLiteralLike(expr)) {
|
||||
return true;
|
||||
}
|
||||
else if (expr.kind === SyntaxKind.BinaryExpression) {
|
||||
@@ -7947,6 +7947,7 @@ namespace ts {
|
||||
switch (expr.kind) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return true;
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
return (<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusToken &&
|
||||
@@ -7969,7 +7970,7 @@ namespace ts {
|
||||
for (const declaration of symbol.declarations) {
|
||||
if (declaration.kind === SyntaxKind.EnumDeclaration) {
|
||||
for (const member of (<EnumDeclaration>declaration).members) {
|
||||
if (member.initializer && member.initializer.kind === SyntaxKind.StringLiteral) {
|
||||
if (member.initializer && isStringLiteralLike(member.initializer)) {
|
||||
return links.enumKind = EnumKind.Literal;
|
||||
}
|
||||
if (!isLiteralEnumMember(member)) {
|
||||
@@ -17921,7 +17922,7 @@ namespace ts {
|
||||
|
||||
function getAccessedPropertyName(access: AccessExpression): __String | undefined {
|
||||
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
|
||||
isStringLiteral(access.argumentExpression) || isNumericLiteral(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
|
||||
isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
|
||||
undefined;
|
||||
}
|
||||
|
||||
@@ -18319,8 +18320,8 @@ namespace ts {
|
||||
const witnesses: (string | undefined)[] = [];
|
||||
for (const clause of switchStatement.caseBlock.clauses) {
|
||||
if (clause.kind === SyntaxKind.CaseClause) {
|
||||
if (clause.expression.kind === SyntaxKind.StringLiteral) {
|
||||
witnesses.push((clause.expression as StringLiteral).text);
|
||||
if (isStringLiteralLike(clause.expression)) {
|
||||
witnesses.push(clause.expression.text);
|
||||
continue;
|
||||
}
|
||||
return emptyArray;
|
||||
@@ -22887,7 +22888,7 @@ namespace ts {
|
||||
return objectType;
|
||||
}
|
||||
|
||||
if (isConstEnumObjectType(objectType) && indexExpression.kind !== SyntaxKind.StringLiteral) {
|
||||
if (isConstEnumObjectType(objectType) && !isStringLiteralLike(indexExpression)) {
|
||||
error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal);
|
||||
return errorType;
|
||||
}
|
||||
@@ -31603,7 +31604,8 @@ namespace ts {
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.StringLiteral:
|
||||
return (<StringLiteral>expr).text;
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return (<StringLiteralLike>expr).text;
|
||||
case SyntaxKind.NumericLiteral:
|
||||
checkGrammarNumericLiteral(<NumericLiteral>expr);
|
||||
return +(<NumericLiteral>expr).text;
|
||||
@@ -31656,7 +31658,7 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.Identifier ||
|
||||
node.kind === SyntaxKind.PropertyAccessExpression && isConstantMemberAccess((<PropertyAccessExpression>node).expression) ||
|
||||
node.kind === SyntaxKind.ElementAccessExpression && isConstantMemberAccess((<ElementAccessExpression>node).expression) &&
|
||||
(<ElementAccessExpression>node).argumentExpression.kind === SyntaxKind.StringLiteral;
|
||||
isStringLiteralLike((<ElementAccessExpression>node).argumentExpression);
|
||||
}
|
||||
|
||||
function checkEnumDeclaration(node: EnumDeclaration) {
|
||||
@@ -35239,7 +35241,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isStringOrNumberLiteralExpression(expr: Expression) {
|
||||
return expr.kind === SyntaxKind.StringLiteral || expr.kind === SyntaxKind.NumericLiteral ||
|
||||
return isStringOrNumericLiteralLike(expr) ||
|
||||
expr.kind === SyntaxKind.PrefixUnaryExpression && (<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusToken &&
|
||||
(<PrefixUnaryExpression>expr).operand.kind === SyntaxKind.NumericLiteral;
|
||||
}
|
||||
|
||||
@@ -3249,9 +3249,10 @@ namespace ts {
|
||||
|
||||
const substitute = createLiteral(constantValue);
|
||||
if (!compilerOptions.removeComments) {
|
||||
const propertyName = isPropertyAccessExpression(node)
|
||||
? declarationNameToString(node.name)
|
||||
: getTextOfNode(node.argumentExpression);
|
||||
const originalNode = getOriginalNode(node, isAccessExpression);
|
||||
const propertyName = isPropertyAccessExpression(originalNode)
|
||||
? declarationNameToString(originalNode.name)
|
||||
: getTextOfNode(originalNode.argumentExpression);
|
||||
|
||||
addSyntheticTrailingComment(substitute, SyntaxKind.MultiLineCommentTrivia, ` ${propertyName} `);
|
||||
}
|
||||
|
||||
@@ -1692,7 +1692,6 @@ namespace ts {
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
case SyntaxKind.SpreadElement:
|
||||
case SyntaxKind.TemplateExpression:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.OmittedExpression:
|
||||
case SyntaxKind.JsxElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
@@ -1715,6 +1714,7 @@ namespace ts {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.BigIntLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
return isInExpressionContext(node);
|
||||
default:
|
||||
@@ -2529,6 +2529,7 @@ namespace ts {
|
||||
const parent = name.parent;
|
||||
switch (name.kind) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
if (isComputedPropertyName(parent)) return parent.parent;
|
||||
// falls through
|
||||
@@ -2556,7 +2557,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isLiteralComputedPropertyDeclarationName(node: Node) {
|
||||
return (node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NumericLiteral) &&
|
||||
return isStringOrNumericLiteralLike(node) &&
|
||||
node.parent.kind === SyntaxKind.ComputedPropertyName &&
|
||||
isDeclaration(node.parent.parent);
|
||||
}
|
||||
@@ -3243,20 +3244,22 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip off existed single quotes or double quotes from a given string
|
||||
* Strip off existed surrounding single quotes, double quotes, or backticks from a given string
|
||||
*
|
||||
* @return non-quoted string
|
||||
*/
|
||||
export function stripQuotes(name: string) {
|
||||
const length = name.length;
|
||||
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && startsWithQuote(name)) {
|
||||
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0))) {
|
||||
return name.substring(1, length - 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
export function startsWithQuote(name: string): boolean {
|
||||
return isSingleOrDoubleQuote(name.charCodeAt(0));
|
||||
function isQuoteOrBacktick(charCode: number) {
|
||||
return charCode === CharacterCodes.singleQuote ||
|
||||
charCode === CharacterCodes.doubleQuote ||
|
||||
charCode === CharacterCodes.backtick;
|
||||
}
|
||||
|
||||
function getReplacement(c: string, offset: number, input: string) {
|
||||
|
||||
@@ -2086,7 +2086,7 @@ namespace ts.Completions {
|
||||
if (name === undefined
|
||||
// If the symbol is external module, don't show it in the completion list
|
||||
// (i.e declare module "http" { const x; } | // <= request completion here, "http" should not be there)
|
||||
|| symbol.flags & SymbolFlags.Module && startsWithQuote(name)
|
||||
|| symbol.flags & SymbolFlags.Module && isSingleOrDoubleQuote(name.charCodeAt(0))
|
||||
// If the symbol is the internal name of an ES symbol, it is not a valid entry. Internal names for ES symbols start with "__@"
|
||||
|| isKnownSymbol(symbol)) {
|
||||
return undefined;
|
||||
|
||||
@@ -448,7 +448,7 @@ namespace ts.FindAllReferences {
|
||||
function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSpan {
|
||||
let start = node.getStart(sourceFile);
|
||||
let end = (endNode || node).getEnd();
|
||||
if (node.kind === SyntaxKind.StringLiteral) {
|
||||
if (isStringLiteralLike(node)) {
|
||||
Debug.assert(endNode === undefined);
|
||||
start += 1;
|
||||
end -= 1;
|
||||
@@ -1234,8 +1234,9 @@ namespace ts.FindAllReferences.Core {
|
||||
case SyntaxKind.Identifier:
|
||||
return (node as Identifier).text.length === searchSymbolName.length;
|
||||
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.StringLiteral: {
|
||||
const str = node as StringLiteral;
|
||||
const str = node as StringLiteralLike;
|
||||
return (isLiteralNameOfPropertyDeclarationOrIndexAccess(str) || isNameOfModuleDeclaration(node) || isExpressionOfExternalModuleImportEqualsDeclaration(node) || (isCallExpression(node.parent) && isBindableObjectDefinePropertyCall(node.parent) && node.parent.arguments[1] === node)) &&
|
||||
str.text.length === searchSymbolName.length;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace ts.Rename {
|
||||
function createTriggerSpanForNode(node: Node, sourceFile: SourceFile) {
|
||||
let start = node.getStart(sourceFile);
|
||||
let width = node.getWidth(sourceFile);
|
||||
if (node.kind === SyntaxKind.StringLiteral) {
|
||||
if (isStringLiteralLike(node)) {
|
||||
// Exclude the quotes
|
||||
start += 1;
|
||||
width -= 2;
|
||||
@@ -93,6 +93,7 @@ namespace ts.Rename {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
return true;
|
||||
case SyntaxKind.NumericLiteral:
|
||||
|
||||
@@ -2231,6 +2231,7 @@ namespace ts {
|
||||
function getContainingObjectLiteralElementWorker(node: Node): ObjectLiteralElement | undefined {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
if (node.parent.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined;
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace ts {
|
||||
isFunctionLike(node.parent) && (<FunctionLikeDeclaration>node.parent).name === node;
|
||||
}
|
||||
|
||||
export function isLiteralNameOfPropertyDeclarationOrIndexAccess(node: StringLiteral | NumericLiteral): boolean {
|
||||
export function isLiteralNameOfPropertyDeclarationOrIndexAccess(node: StringLiteral | NumericLiteral | NoSubstitutionTemplateLiteral): boolean {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
|
||||
Reference in New Issue
Block a user