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 all 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