From 3dc40d0200e317275ff472aac7a00607146b1283 Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Tue, 16 Apr 2019 19:58:29 +0200 Subject: [PATCH] Improve error message for restrict-plus-operands rule (#4635) --- src/rules/restrictPlusOperandsRule.ts | 31 ++++++++++------- .../rules/restrict-plus-operands/test.ts.lint | 33 ++++++++++--------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/rules/restrictPlusOperandsRule.ts b/src/rules/restrictPlusOperandsRule.ts index 207ec49a56f..8ad61e2090c 100644 --- a/src/rules/restrictPlusOperandsRule.ts +++ b/src/rules/restrictPlusOperandsRule.ts @@ -37,7 +37,7 @@ export class Rule extends Lint.Rules.TypedRule { public static INVALID_TYPES_ERROR = "Operands of '+' operation must either be both strings or both numbers"; - public static SUGGEST_TEMPLATE_LITERALS = ", consider using template literals"; + public static SUGGEST_TEMPLATE_LITERALS = ". Consider using template literals."; public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker()); @@ -47,23 +47,32 @@ export class Rule extends Lint.Rules.TypedRule { function walk(ctx: Lint.WalkContext, tc: ts.TypeChecker) { return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void { if (isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) { - const leftType = getBaseTypeOfLiteralType(tc.getTypeAtLocation(node.left)); - const rightType = getBaseTypeOfLiteralType(tc.getTypeAtLocation(node.right)); - if (leftType === "invalid" || rightType === "invalid" || leftType !== rightType) { - if (leftType === "string" || rightType === "string") { - return ctx.addFailureAtNode( - node, - Rule.INVALID_TYPES_ERROR + Rule.SUGGEST_TEMPLATE_LITERALS, - ); - } else { - return ctx.addFailureAtNode(node, Rule.INVALID_TYPES_ERROR); + const leftType = tc.getTypeAtLocation(node.left); + const leftTypeStr = getBaseTypeOfLiteralType(leftType); + const rightType = tc.getTypeAtLocation(node.right); + const rightTypeStr = getBaseTypeOfLiteralType(rightType); + if (leftTypeStr === "invalid" || rightTypeStr === "invalid" || leftTypeStr !== rightTypeStr) { + const actualTypes = `, but found ${getTypeString(tc, node.left, leftType)} + ${getTypeString(tc, node.right, rightType)}`; + let message = Rule.INVALID_TYPES_ERROR + actualTypes; + if (leftTypeStr === "string" || rightTypeStr === "string") { + message += Rule.SUGGEST_TEMPLATE_LITERALS; } + return ctx.addFailureAtNode(node, message); } } return ts.forEachChild(node, cb); }); } +function getTypeString(tc: ts.TypeChecker, node: ts.Node, type: ts.Type) { + const typeString = tc.typeToString(type, node); + if (typeString === "undefined[]" && ts.isArrayLiteralExpression(node) && !node.elements.length) { + // Special case literal "[]" arrays that would otherwise be emitted as undefined[]. + return "[]"; + } + return typeString; +} + function getBaseTypeOfLiteralType(type: ts.Type): "string" | "number" | "invalid" { if ( isTypeFlagSet(type, ts.TypeFlags.StringLiteral) || diff --git a/test/rules/restrict-plus-operands/test.ts.lint b/test/rules/restrict-plus-operands/test.ts.lint index 18ef4acdd28..4899d42ced7 100644 --- a/test/rules/restrict-plus-operands/test.ts.lint +++ b/test/rules/restrict-plus-operands/test.ts.lint @@ -17,35 +17,38 @@ var pair: NumberStringPair = { // bad var bad1 = 5 + "10"; - ~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found 5 + "10". Consider using template literals.] var bad2 = [] + 5; - ~~~~~~ [Operands of '+' operation must either be both strings or both numbers] + ~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found [] + 5] var bad3 = [] + {}; - ~~~~~~~ [Operands of '+' operation must either be both strings or both numbers] + ~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found [] + {}] var bad4 = [] + []; - ~~~~~~~ [Operands of '+' operation must either be both strings or both numbers] + ~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found [] + []] var bad4 = 5 + []; - ~~~~~~ [Operands of '+' operation must either be both strings or both numbers] + ~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found 5 + []] var bad5 = "5" + {}; - ~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found "5" + {}. Consider using template literals.] var bad6 = 5.5 + "5"; - ~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found 5.5 + "5". Consider using template literals.] var bad7 = "5.5" + 5; - ~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found "5.5" + 5. Consider using template literals.] var bad8 = x + y; - ~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found number + string. Consider using template literals.] var bad9 = y + x; - ~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found string + number. Consider using template literals.] var bad10 = x + {}; - ~~~~~~ [Operands of '+' operation must either be both strings or both numbers] + ~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found number + {}] var bad11 = [] + y; - ~~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found [] + string. Consider using template literals.] var bad12 = pair.first + "10"; - ~~~~~~~~~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~~~~~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found number + "10". Consider using template literals.] var bad13 = 5 + pair.second; - ~~~~~~~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, consider using template literals] + ~~~~~~~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found 5 + string. Consider using template literals.] var bad14 = pair + pair; - ~~~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers] + ~~~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found NumberStringPair + NumberStringPair] +var anyTyped: any = 5; +var bad15 = anyTyped + 12; + ~~~~~~~~~~~~~ [Operands of '+' operation must either be both strings or both numbers, but found any + 12] // good var good1 = 5 + 10;