Skip to content

Commit

Permalink
New: space-infix-ops rule (bradzacher#156)
Browse files Browse the repository at this point in the history
  • Loading branch information
madbence committed Oct 6, 2018
1 parent 5e410a3 commit 7629a05
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -73,5 +73,6 @@ Then configure the rules you want to use under the rules section.
* [`typescript/no-use-before-define`](./docs/rules/no-use-before-define.md) — Disallow the use of variables before they are defined
* [`typescript/no-var-requires`](./docs/rules/no-var-requires.md) — Disallows the use of require statements except in import statements (`no-var-requires` from TSLint)
* [`typescript/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) — Require the use of the `namespace` keyword instead of the `module` keyword to declare custom TypeScript modules. (`no-internal-module` from TSLint)
* [`typescript/space-infix-ops`](./docs/rules/space-infix-ops.md) — Require spacing around infix operators in TypeScript-specific constructs (to be used in tandem with the code `space-infix-ops` rule)
* [`typescript/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) — Require consistent spacing around type annotations
<!-- end rule list -->
40 changes: 40 additions & 0 deletions docs/rules/space-infix-ops.md
@@ -0,0 +1,40 @@
# Require spacing around infix operators in TypeScript-specific constructs (to be used in tandem with the code `space-infix-ops` rule) (space-infix-ops)

While formatting preferences are very personal, a number of style guides require spaces around operators, such as:

```ts
type id = number;
type Perhaps<T> = T | null;
```

This rule is intended to be used together with the core rule [`space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops).

## Rule Details

This rule aims to enforce spaces around infix operators in TypeScript-specific constructs.

Examples of **incorrect** code:

```ts
type id=number;
type id =number;
type id= number;
type Perhaps<T>=T | null;
type Perhaps<T> =T | null;
type Perhaps<T>= T | null;
```

Examples of **correct** code:
```ts
type id = number;
type Perhaps<T> = T | null;
```

## When Not To Use It

If you don't want to enforce spacing for infix ops, you can safely turn this rule off.

## Further Reading

* [Advanced Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html)
* [core `space-infix-ops` rule](https://eslint.org/docs/rules/space-infix-ops)
110 changes: 110 additions & 0 deletions lib/rules/space-infix-ops.js
@@ -0,0 +1,110 @@
/**
* @fileoverview Require spaces around infix operators
* @author Michael Ficarra
* @author Bence Dányi
*/
"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description:
"Require spacing around infix operators in TypeScript-specific constructs (to be used in tandem with the code `space-infix-ops` rule)",
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/space-infix-ops.md"
},
fixable: "whitespace"
},

create(context) {
const sourceCode = context.getSourceCode();

/**
* Returns the first token which violates the rule
* @param {ASTNode} left - The left node of the main node
* @param {ASTNode} right - The right node of the main node
* @returns {Object} The violator token or null
* @private
*/
function getFirstNonSpacedToken(left, right) {
const tokens = sourceCode.getTokensBetween(left, right, 1);

for (let i = 1, l = tokens.length - 1; i < l; ++i) {
const op = tokens[i];

if (
op.type === "Punctuator" &&
op.value === "=" &&
(tokens[i - 1].range[1] >= op.range[0] ||
op.range[1] >= tokens[i + 1].range[0])
) {
return op;
}
}
return null;
}

/**
* Reports an AST node as a rule violation
* @param {ASTNode} mainNode - The node to report
* @param {Object} culpritToken - The token which has a problem
* @returns {void}
* @private
*/
function report(mainNode, culpritToken) {
context.report({
node: mainNode,
loc: culpritToken.loc.start,
message: "Infix operators must be spaced.",
fix(fixer) {
const previousToken = sourceCode.getTokenBefore(
culpritToken
);
const afterToken = sourceCode.getTokenAfter(culpritToken);
let fixString = "";

if (culpritToken.range[0] - previousToken.range[1] === 0) {
fixString = " ";
}

fixString += culpritToken.value;

if (afterToken.range[0] - culpritToken.range[1] === 0) {
fixString += " ";
}

return fixer.replaceText(culpritToken, fixString);
}
});
}

/**
* Check if the node is a type alias
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function checkAlias(node) {
if (node.parent.kind !== "type") return;
const leftNode = node.typeParameters
? node.typeParameters
: node.id;
const rightNode = node.init;
const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode);

if (nonSpacedNode) {
report(node, nonSpacedNode);
}
}

return {
VariableDeclarator: checkAlias
};
}
};
86 changes: 86 additions & 0 deletions tests/lib/rules/space-infix-ops.js
@@ -0,0 +1,86 @@
/**
* @fileoverview Enforces spacing around type annotations
* @author Nicholas C. Zakas
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../lib/rules/space-infix-ops"),
RuleTester = require("eslint").RuleTester;

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
parser: "typescript-eslint-parser"
});

ruleTester.run("space-infix-ops", rule, {
valid: ["type Foo = string;", "type Foo<T> = T;"],
invalid: [
{
code: "type Foo=number;",
errors: [
{
message: "Infix operators must be spaced.",
line: 1,
column: 9
}
]
},
{
code: "type Foo =number;",
errors: [
{
message: "Infix operators must be spaced.",
line: 1,
column: 10
}
]
},
{
code: "type Foo= number;",
errors: [
{
message: "Infix operators must be spaced.",
line: 1,
column: 9
}
]
},
{
code: "type Foo<T>=T;",
errors: [
{
message: "Infix operators must be spaced.",
line: 1,
column: 12
}
]
},
{
code: "type Foo<T> =T;",
errors: [
{
message: "Infix operators must be spaced.",
line: 1,
column: 13
}
]
},
{
code: "type Foo<T>= T;",
errors: [
{
message: "Infix operators must be spaced.",
line: 1,
column: 12
}
]
}
]
});

0 comments on commit 7629a05

Please sign in to comment.