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

Add ArrayType rule to enforce 'T[]' or 'Array<T>' #1498

Merged
merged 1 commit into from
Aug 24, 2016
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/language/walker/syntaxWalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export class SyntaxWalker {
this.walkChildren(node);
}

protected visitArrayType(node: ts.ArrayTypeNode) {
this.walkChildren(node);
}

protected visitArrowFunction(node: ts.FunctionLikeDeclaration) {
this.walkChildren(node);
}
Expand Down Expand Up @@ -328,6 +332,10 @@ export class SyntaxWalker {
this.visitArrayLiteralExpression(<ts.ArrayLiteralExpression> node);
break;

case ts.SyntaxKind.ArrayType:
this.visitArrayType(<ts.ArrayTypeNode> node);
break;

case ts.SyntaxKind.ArrowFunction:
this.visitArrowFunction(<ts.FunctionLikeDeclaration> node);
break;
Expand Down
76 changes: 76 additions & 0 deletions src/rules/arrayTypeRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as ts from "typescript";

import * as Lint from "../lint";

const OPTION_ARRAY = "array";
const OPTION_GENERIC = "generic";

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "array-type",
description: "Requires using either 'T[]' or 'Array<T>' for arrays.",
optionsDescription: Lint.Utils.dedent`
One of the following arguments must be provided:

* \`"${OPTION_ARRAY}"\` enforces use of 'T[]'.
* \`"${OPTION_GENERIC}"\` enforces use of 'Array<T>'.`,
options: {
type: "string",
enum: [OPTION_ARRAY, OPTION_GENERIC],
},
optionExamples: ["[true, array]", "[true, generic]"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the OPTION_ variables here as well (no need to make a commit just to change this though)

type: "style",
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING_ARRAY = "Array type using 'Array<T>' is forbidden. Use 'T[]' instead.";
public static FAILURE_STRING_GENERIC = "Array type using 'T[]' is forbidden. Use 'Array<T>' instead.";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const alignWalker = new ArrayTypeWalker(sourceFile, this.getOptions());
return this.applyWithWalker(alignWalker);
}
}

class ArrayTypeWalker extends Lint.RuleWalker {
public visitArrayType(node: ts.ArrayTypeNode) {
if (this.hasOption(OPTION_GENERIC)) {
const typeName = node.elementType;
const fix = new Lint.Fix(Rule.metadata.ruleName, [
this.appendText(typeName.getStart(), "Array<"),
// Delete the square brackets and replace with an angle bracket
this.createReplacement(typeName.getEnd(), node.getEnd() - typeName.getEnd(), ">"),
]);
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING_GENERIC, fix));
}

super.visitArrayType(node);
}

public visitTypeReference(node: ts.TypeReferenceNode) {
const typeName = node.typeName.getText();
if (this.hasOption(OPTION_ARRAY) && typeName === "Array") {
const typeArgs = node.typeArguments;
let fix: Lint.Fix;
if (!typeArgs || typeArgs.length === 0) {
// Create an 'any' array
fix = new Lint.Fix(Rule.metadata.ruleName, [
this.createReplacement(node.getStart(), node.getWidth(), "any[]"),
]);
} else if (typeArgs && typeArgs.length === 1) {
const typeStart = typeArgs[0].getStart();
const typeEnd = typeArgs[0].getEnd();
fix = new Lint.Fix(Rule.metadata.ruleName, [
// Delete Array and the first angle bracket
this.deleteText(node.getStart(), typeStart - node.getStart()),
// Delete the last angle bracket and replace with square brackets
this.createReplacement(typeEnd, node.getEnd() - typeEnd, "[]"),
]);
}
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING_ARRAY, fix));
}

super.visitTypeReference(node);
}
}
1 change: 1 addition & 0 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"language/walker/syntaxWalker.ts",
"rules/adjacentOverloadSignaturesRule.ts",
"rules/alignRule.ts",
"rules/arrayTypeRule.ts",
"rules/arrowParensRule.ts",
"rules/banRule.ts",
"rules/classNameRule.ts",
Expand Down
29 changes: 29 additions & 0 deletions test/rules/array-type/array/test.ts.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
let x: number[] = [1] as number[];
let y: string[] = <string[]>["2"];
let z: any[] = [3, "4"];

let xx: number[][] = [[1, 2], [3]];
let yy: number[][] = [[4, 5], [6]];

type Arr<T> = T[];

// Ignore user defined aliases
let yyyy: Arr<Arr<string>[][]> = [[[["2"]]]];

interface ArrayClass<T> {
foo: T[];
bar: T[];
baz: Arr<T>;
}

function fooFunction(foo: ArrayClass<string>[]) {
return foo.map(e => e.foo);
}

function barFunction(bar: ArrayClass<String>[]) {
return bar.map(e => e.bar);
}

function bazFunction(baz: Arr<ArrayClass<String>>) {
return baz.map(e => e.baz);
}
38 changes: 38 additions & 0 deletions test/rules/array-type/array/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
let x: Array<number> = [1] as number[];
~~~~~~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]
let y: string[] = <Array<string>>["2"];
~~~~~~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]
let z: Array = [3, "4"];
~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]

let xx: Array<Array<number>> = [[1, 2], [3]];
~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]
~~~~~~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]
let yy: number[][] = [[4, 5], [6]];

type Arr<T> = Array<T>;
~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]

// Ignore user defined aliases
let yyyy: Arr<Array<Arr<string>>[]> = [[[["2"]]]];
~~~~~~~~~~~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]

interface ArrayClass<T> {
foo: Array<T>;
~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]
bar: T[];
baz: Arr<T>;
}

function fooFunction(foo: Array<ArrayClass<string>>) {
~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array<T>' is forbidden. Use 'T[]' instead.]
return foo.map(e => e.foo);
}

function barFunction(bar: ArrayClass<String>[]) {
return bar.map(e => e.bar);
}

function bazFunction(baz: Arr<ArrayClass<String>>) {
return baz.map(e => e.baz);
}
5 changes: 5 additions & 0 deletions test/rules/array-type/array/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"array-type": [true, "array"]
}
}
29 changes: 29 additions & 0 deletions test/rules/array-type/generic/test.ts.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
let x: Array<number> = [1] as Array<number>;
let y: Array<string> = <Array<string>>["2"];
let z: Array = [3, "4"];

let xx: Array<Array<number>> = [[1, 2], [3]];
let yy: Array<Array<number>> = [[4, 5], [6]];

type Arr<T> = Array<T>;

// Ignore user defined aliases
let yyyy: Arr<Array<Array<Arr<string>>>> = [[[["2"]]]];

interface ArrayClass<T> {
foo: Array<T>;
bar: Array<T>;
baz: Arr<T>;
}

function fooFunction(foo: Array<ArrayClass<string>>) {
return foo.map(e => e.foo);
}

function barFunction(bar: Array<ArrayClass<String>>) {
return bar.map(e => e.bar);
}

function bazFunction(baz: Arr<ArrayClass<String>>) {
return baz.map(e => e.baz);
}
36 changes: 36 additions & 0 deletions test/rules/array-type/generic/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
let x: Array<number> = [1] as number[];
~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array<T>' instead.]
let y: string[] = <Array<string>>["2"];
~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array<T>' instead.]
let z: Array = [3, "4"];

let xx: Array<Array<number>> = [[1, 2], [3]];
let yy: number[][] = [[4, 5], [6]];
~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array<T>' instead.]
~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array<T>' instead.]

type Arr<T> = Array<T>;

// Ignore user defined aliases
let yyyy: Arr<Array<Arr<string>>[]> = [[[["2"]]]];
~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array<T>' instead.]

interface ArrayClass<T> {
foo: Array<T>;
bar: T[];
~~~ [Array type using 'T[]' is forbidden. Use 'Array<T>' instead.]
baz: Arr<T>;
}

function fooFunction(foo: Array<ArrayClass<string>>) {
return foo.map(e => e.foo);
}

function barFunction(bar: ArrayClass<String>[]) {
~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array<T>' instead.]
return bar.map(e => e.bar);
}

function bazFunction(baz: Arr<ArrayClass<String>>) {
return baz.map(e => e.baz);
}
5 changes: 5 additions & 0 deletions test/rules/array-type/generic/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"array-type": [true, "generic"]
}
}
1 change: 1 addition & 0 deletions test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"../src/rules.ts",
"../src/rules/adjacentOverloadSignaturesRule.ts",
"../src/rules/alignRule.ts",
"../src/rules/arrayTypeRule.ts",
"../src/rules/arrowParensRule.ts",
"../src/rules/banRule.ts",
"../src/rules/classNameRule.ts",
Expand Down