Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Commit

Permalink
Include actual types for restrict-plus-operands.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mprobst committed Apr 3, 2019
1 parent 0289e6d commit a845e63
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 26 deletions.
22 changes: 11 additions & 11 deletions src/rules/restrictPlusOperandsRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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);
Expand Down
33 changes: 18 additions & 15 deletions test/rules/restrict-plus-operands/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit a845e63

Please sign in to comment.