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

Include actual types for restrict-plus-operands. #4635

Merged
merged 4 commits into from Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 20 additions & 11 deletions src/rules/restrictPlusOperandsRule.ts
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.";
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
Expand All @@ -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) ||
Expand Down
33 changes: 18 additions & 15 deletions test/rules/restrict-plus-operands/test.ts.lint
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 [] + 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;
Expand Down