Skip to content
This repository has been archived by the owner on Jan 19, 2019. It is now read-only.

Commit

Permalink
Fix: issues with array-type rule
Browse files Browse the repository at this point in the history
  • Loading branch information
armano2 committed Dec 11, 2018
1 parent d76408d commit ad63920
Show file tree
Hide file tree
Showing 4 changed files with 997 additions and 46 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ This guarantees 100% compatibility between the plugin and the parser.
<!-- Please run `npm run docs` to update this section -->
<!-- begin rule list -->
* [`typescript/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) — Require that member overloads be consecutive
* [`typescript/array-type`](./docs/rules/array-type.md) — Requires using either 'T[]' or 'Array' for arrays (`array-type` from TSLint)
* [`typescript/camelcase`](./docs/rules/camelcase.md) — Enforce camelCase naming convention
* [`typescript/class-name-casing`](./docs/rules/class-name-casing.md) — Require PascalCased class and interface names (`class-name` from TSLint)
* [`typescript/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) — Require explicit return types on functions and class methods
Expand Down
1 change: 1 addition & 0 deletions docs/rules/array-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Requires using either 'T[]' or 'Array' for arrays (array-type)
198 changes: 179 additions & 19 deletions lib/rules/array-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,63 @@

const util = require("../util");

function isSimpleType(node) {
switch (node.type) {
case "Identifier":
case "TSAnyKeyword":
case "TSBooleanKeyword":
case "TSNeverKeyword":
case "TSNumberKeyword":
case "TSObjectKeyword":
case "TSStringKeyword":
case "TSSymbolKeyword":
case "TSUnknownKeyword":
case "TSVoidKeyword":
case "TSNullKeyword":
case "TSArrayType":
case "TSUndefinedKeyword":
case "TSThisType":
case "TSQualifiedName":
return true;
case "TSTypeReference":
if (
node.typeName &&
node.typeName.type === "Identifier" &&
node.typeName.name === "Array"
) {
if (!node.typeParameters) {
return true;
}
if (node.typeParameters.params.length === 1) {
return isSimpleType(node.typeParameters.params[0]);
}
} else {
if (node.typeParameters) {
return false;
}
return isSimpleType(node.typeName);
}
return false;
default:
return false;
}
}

function typeNeedsParentheses(node) {
if (node.type === "TSTypeReference") {
switch (node.typeName.type) {
case "TSUnionType":
case "TSFunctionType":
case "TSIntersectionType":
case "TSTypeOperator":
return true;
default:
return false;
}
}
return false;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
Expand All @@ -17,42 +74,145 @@ module.exports = {
extraDescription: [util.tslintRule("array-type")],
category: "TypeScript",
url:
"https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/array-type.md"
"https://github.com/nzakas/eslint-plugin-typescript/blob/master/docs/rules/array-type.md",
},
fixable: "code",
messages: {
errorStringGeneric:
"Array type using 'T[]' is forbidden. Use 'Array<T>' instead.",
errorStringGenericSimple:
"Array type using 'T[]' is forbidden for non-simple types. Use 'Array<T>' instead.",
errorStringArray:
"Array type using 'Array<T>' is forbidden. Use 'T[]' instead.",
errorStringArraySimple:
"Array type using 'Array<T>' is forbidden for simple types. Use 'T[]' instead.",
},
schema: [
{
enum: ["array", "generic", "array-simple"]
}
]
enum: ["array", "generic", "array-simple"],
},
],
},
create(context) {
const arrayType = context.options[0] || "array";
const option = context.options[0] || "array";
const sourceCode = context.getSourceCode();

function requireWhitespaceBefore(node) {
const prevToken = sourceCode.getTokenBefore(node);

if (node.range[0] - prevToken.range[1] > 0) {
return false;
}

return prevToken.type === "Identifier";
}

//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------

return {
TypeAnnotation: console.log, // eslint-disable-line no-console
TSArrayType(node) {
console.log(node); // eslint-disable-line no-console
if (arrayType === "generic") {
context.report({
node,
message:
"Array type using 'T[]' is forbidden. Use 'Array<T>' instead."
});
if (
option === "array" ||
(option === "array-simple" &&
isSimpleType(node.elementType))
) {
return;
}
const messageId =
option === "generic"
? "errorStringGeneric"
: "errorStringGenericSimple";

context.report({
node,
messageId,
fix(fixer) {
const startText = requireWhitespaceBefore(node);
const toFix = [
fixer.replaceTextRange(
[node.range[1] - 2, node.range[1]],
">"
),
fixer.insertTextBefore(
node,
`${startText ? " " : ""}Array<`
),
];

if (node.elementType.type === "TSParenthesizedType") {
toFix.push(
fixer.remove(
sourceCode.getFirstToken(node.elementType)
)
);
toFix.push(
fixer.remove(
sourceCode.getLastToken(node.elementType)
)
);
}

return toFix;
},
});
},
TSTypeReference(node) {
console.log(node); // eslint-disable-line no-console
if (node.typeName.name === "Array" && arrayType === "array") {
if (
option === "generic" ||
node.typeName.type !== "Identifier" ||
node.typeName.name !== "Array"
) {
return;
}
const messageId =
option === "array"
? "errorStringArray"
: "errorStringArraySimple";

const typeParams =
node.typeParameters && node.typeParameters.params;

if (!typeParams || typeParams.length === 0) {
// Create an 'any' array
context.report({
node,
message:
"Array type using 'Array<T>' is forbidden. Use 'T[]' instead."
messageId,
fix(fixer) {
return fixer.replaceText(node, "any[]");
},
});
return;
}
}

if (
typeParams.length !== 1 ||
(option === "array-simple" && !isSimpleType(typeParams[0]))
) {
return;
}

const type = typeParams[0];
const parens = typeNeedsParentheses(type);

context.report({
node,
messageId,
fix(fixer) {
return [
fixer.replaceTextRange(
[node.range[0], type.range[0]],
parens ? "(" : ""
),
fixer.replaceTextRange(
[type.range[1], node.range[1]],
parens ? ")[]" : "[]"
),
];
},
});
},
};
}
},
};
Loading

0 comments on commit ad63920

Please sign in to comment.