From a845e637f83e7457f936ca1b66fd444fe6bb08fb Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Wed, 3 Apr 2019 07:49:45 -0700 Subject: [PATCH] Include actual types for restrict-plus-operands. It can be difficult for users to find out why exactly they received the "restrict-plus-operands" lint error. In particular, users often end up with `any` types leaking into their code (e.g. in tests), and are then puzzled why this triggers. To fix, this change includes a textual representation of the type in the error message, e.g.: [Operands of '+' operation must either be both strings or both numbers, but found 5 + undefined[]] The type representation isn't always super pretty (e.g. the `undefined[]` bit above), but should still be helpful, and also matches TS compiler's representation of these. --- src/rules/restrictPlusOperandsRule.ts | 22 ++++++------- .../rules/restrict-plus-operands/test.ts.lint | 33 ++++++++++--------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/rules/restrictPlusOperandsRule.ts b/src/rules/restrictPlusOperandsRule.ts index 207ec49a56f..ea6848e8e88 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,17 +47,17 @@ 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 ${tc.typeToString(leftType, node)} + ${tc.typeToString(rightType, node)}`; + 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); diff --git a/test/rules/restrict-plus-operands/test.ts.lint b/test/rules/restrict-plus-operands/test.ts.lint index 18ef4acdd28..44b5ffb8e51 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 undefined[] + 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 undefined[] + {}] 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 undefined[] + undefined[]] 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 + undefined[]] 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 undefined[] + 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;