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

arrow-parens: Allow binding patterns ([x, y]) => ... and ({x, y}) => ... to have parens #1958

Merged
merged 3 commits into from
Jan 1, 2017
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
61 changes: 30 additions & 31 deletions src/rules/arrowParensRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import * as ts from "typescript";

import * as Lint from "../index";
import { hasModifier } from "../language/utils";

const BAN_SINGLE_ARG_PARENS = "ban-single-arg-parens";

Expand All @@ -29,7 +28,7 @@ export class Rule extends Lint.Rules.AbstractRule {
description: "Requires parentheses around the parameters of arrow function definitions.",
rationale: "Maintains stylistic consistency with other arrow function definitions.",
optionsDescription: Lint.Utils.dedent`
if \`${BAN_SINGLE_ARG_PARENS}\` is specified, then arrow functions with one parameter
if \`${BAN_SINGLE_ARG_PARENS}\` is specified, then arrow functions with one parameter
must not have parentheses if removing them is allowed by TypeScript.`,
options: {
type: "string",
Expand Down Expand Up @@ -59,41 +58,41 @@ class ArrowParensWalker extends Lint.RuleWalker {
}

public visitArrowFunction(node: ts.FunctionLikeDeclaration) {
if (node.parameters.length === 1) {
if (node.parameters.length === 1 && node.typeParameters === undefined) {
const parameter = node.parameters[0];
const text = parameter.getText();
const firstToken = node.getFirstToken();
const lastToken = node.getChildAt(2);
const width = text.length;
const position = parameter.getStart();
let isGenerics = false;

// If firstToken is LessThanToken, it would be Generics.
if (firstToken.kind === ts.SyntaxKind.LessThanToken) {
isGenerics = true;
let openParen = node.getFirstToken();
let openParenIndex = 0;
if (openParen.kind === ts.SyntaxKind.AsyncKeyword) {
openParen = node.getChildAt(1);
openParenIndex = 1;
}

if (!isGenerics && !hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword)) {
const hasParens = firstToken.kind === ts.SyntaxKind.OpenParenToken && lastToken.kind === ts.SyntaxKind.CloseParenToken;
if (!hasParens && !this.avoidOnSingleParameter) {
const fix = new Lint.Fix(Rule.metadata.ruleName, [new Lint.Replacement(position, width, `(${parameter.getText()})`)]);
this.addFailureAt(position, width, Rule.FAILURE_STRING_MISSING, fix);
} else if (hasParens
&& this.avoidOnSingleParameter
&& parameter.decorators == null
&& parameter.dotDotDotToken == null
&& parameter.initializer == null
&& parameter.modifiers == null
&& parameter.questionToken == null
&& parameter.type == null) {
const fix = new Lint.Fix(Rule.metadata.ruleName, [
new Lint.Replacement(lastToken.getStart(), 1, ``),
new Lint.Replacement(firstToken.getStart(), 1, ``),
]);
this.addFailureAt(position, width, Rule.FAILURE_STRING_EXISTS, fix);
}
const hasParens = openParen.kind === ts.SyntaxKind.OpenParenToken;
if (!hasParens && !this.avoidOnSingleParameter) {
const fix = new Lint.Fix(Rule.metadata.ruleName, [
this.appendText(parameter.getStart(), "("),
this.appendText(parameter.getEnd(), ")"),
]);
this.addFailureAtNode(parameter, Rule.FAILURE_STRING_MISSING, fix);
} else if (hasParens && this.avoidOnSingleParameter && isSimpleParameter(parameter)) {
// Skip over the parameter to get the closing parenthesis
const closeParen = node.getChildAt(openParenIndex + 2);
const fix = new Lint.Fix(Rule.metadata.ruleName, [
this.deleteText(openParen.getStart(), 1),
this.deleteText(closeParen.getStart(), 1),
]);
this.addFailureAtNode(parameter, Rule.FAILURE_STRING_EXISTS, fix);
}
}
super.visitArrowFunction(node);
}
}

function isSimpleParameter(parameter: ts.ParameterDeclaration): boolean {
return parameter.name.kind === ts.SyntaxKind.Identifier
&& parameter.dotDotDotToken === undefined
&& parameter.initializer === undefined
&& parameter.questionToken === undefined
&& parameter.type === undefined;
}
4 changes: 3 additions & 1 deletion test/rules/arrow-parens/ban-single-arg-parens/test.ts.fix
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// valid case
var a = ([x, y]) => {};
var aa = ({x, y}) => {};
var b = (a: number) => {};
var c = (a, b) => {};
var f = (...rest) => {};
Expand All @@ -16,10 +18,10 @@ var piyo = <T, U>(method: () => T) => {
method();
};
const validAsync = async (param: any) => {};
const validAsync = async (param) => {};

var e = (a => {})(1);
var f = ab => {};

// invalid case
var a = a => {};
const invalidAsync = async param => {};
9 changes: 7 additions & 2 deletions test/rules/arrow-parens/ban-single-arg-parens/test.ts.lint
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// valid case
var a = ([x, y]) => {};
var aa = ({x, y}) => {};
var b = (a: number) => {};
var c = (a, b) => {};
var f = (...rest) => {};
Expand All @@ -16,11 +18,14 @@ var piyo = <T, U>(method: () => T) => {
method();
};
const validAsync = async (param: any) => {};
const validAsync = async (param) => {};

var e = (a => {})(1);
var f = ab => {};

// invalid case
var a = (a) => {};
~ [Parentheses are prohibited around the parameter in this single parameter arrow function]
~ [0]
const invalidAsync = async (param) => {};
~~~~~ [0]

[0]: Parentheses are prohibited around the parameter in this single parameter arrow function