blob: a6329471c7608de29a206aa5a7657e5e2f514392 [file] [log] [blame]
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
const util = __importStar(require("../util"));
/**
* Check whatever node can be considered as simple
* @param node the node to be evaluated.
*/
function isSimpleType(node) {
switch (node.type) {
case experimental_utils_1.AST_NODE_TYPES.Identifier:
case experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSBooleanKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSNeverKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSNumberKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSObjectKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSStringKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSSymbolKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSVoidKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSNullKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSArrayType:
case experimental_utils_1.AST_NODE_TYPES.TSUndefinedKeyword:
case experimental_utils_1.AST_NODE_TYPES.TSThisType:
case experimental_utils_1.AST_NODE_TYPES.TSQualifiedName:
return true;
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference:
if (node.typeName &&
node.typeName.type === experimental_utils_1.AST_NODE_TYPES.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;
}
}
/**
* Check if node needs parentheses
* @param node the node to be evaluated.
*/
function typeNeedsParentheses(node) {
switch (node.type) {
case experimental_utils_1.AST_NODE_TYPES.TSTypeReference:
return typeNeedsParentheses(node.typeName);
case experimental_utils_1.AST_NODE_TYPES.TSUnionType:
case experimental_utils_1.AST_NODE_TYPES.TSFunctionType:
case experimental_utils_1.AST_NODE_TYPES.TSIntersectionType:
case experimental_utils_1.AST_NODE_TYPES.TSTypeOperator:
case experimental_utils_1.AST_NODE_TYPES.TSInferType:
return true;
case experimental_utils_1.AST_NODE_TYPES.Identifier:
return node.name === 'ReadonlyArray';
default:
return false;
}
}
exports.default = util.createRule({
name: 'array-type',
meta: {
type: 'suggestion',
docs: {
description: 'Requires using either `T[]` or `Array<T>` for arrays',
category: 'Stylistic Issues',
recommended: 'error',
},
fixable: 'code',
messages: {
errorStringGeneric: "Array type using '{{type}}[]' is forbidden. Use 'Array<{{type}}>' instead.",
errorStringGenericSimple: "Array type using '{{type}}[]' is forbidden for non-simple types. Use 'Array<{{type}}>' instead.",
errorStringArray: "Array type using 'Array<{{type}}>' is forbidden. Use '{{type}}[]' instead.",
errorStringArraySimple: "Array type using 'Array<{{type}}>' is forbidden for simple types. Use '{{type}}[]' instead.",
},
schema: [
{
enum: ['array', 'generic', 'array-simple'],
},
],
},
defaultOptions: ['array'],
create(context, [option]) {
const sourceCode = context.getSourceCode();
/**
* Check if whitespace is needed before this node
* @param node the node to be evaluated.
*/
function requireWhitespaceBefore(node) {
const prevToken = sourceCode.getTokenBefore(node);
if (!prevToken) {
return false;
}
if (node.range[0] - prevToken.range[1] > 0) {
return false;
}
return prevToken.type === experimental_utils_1.AST_TOKEN_TYPES.Identifier;
}
/**
* @param node the node to be evaluated.
*/
function getMessageType(node) {
if (node) {
if (node.type === experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType) {
return getMessageType(node.typeAnnotation);
}
if (isSimpleType(node)) {
return sourceCode.getText(node);
}
}
return 'T';
}
return {
TSArrayType(node) {
if (option === 'array' ||
(option === 'array-simple' && isSimpleType(node.elementType))) {
return;
}
const messageId = option === 'generic'
? 'errorStringGeneric'
: 'errorStringGenericSimple';
const isReadonly = node.parent &&
node.parent.type === experimental_utils_1.AST_NODE_TYPES.TSTypeOperator &&
node.parent.operator === 'readonly';
const typeOpNode = isReadonly ? node.parent : null;
context.report({
node: isReadonly ? node.parent : node,
messageId,
data: {
type: getMessageType(node.elementType),
},
fix(fixer) {
const startText = requireWhitespaceBefore(node);
const toFix = [
fixer.replaceTextRange([node.range[1] - 2, node.range[1]], '>'),
fixer.insertTextBefore(node, `${startText ? ' ' : ''}${isReadonly ? 'Readonly' : ''}Array<`),
];
if (typeOpNode) {
// remove the readonly operator if it exists
toFix.unshift(fixer.removeRange([
typeOpNode.range[0],
typeOpNode.range[0] + 'readonly '.length,
]));
}
if (node.elementType.type === experimental_utils_1.AST_NODE_TYPES.TSParenthesizedType) {
const first = sourceCode.getFirstToken(node.elementType);
const last = sourceCode.getLastToken(node.elementType);
if (!first || !last) {
return null;
}
toFix.push(fixer.remove(first));
toFix.push(fixer.remove(last));
}
return toFix;
},
});
},
TSTypeReference(node) {
if (option === 'generic' ||
node.typeName.type !== experimental_utils_1.AST_NODE_TYPES.Identifier) {
return;
}
if (!['Array', 'ReadonlyArray'].includes(node.typeName.name)) {
return;
}
const messageId = option === 'array' ? 'errorStringArray' : 'errorStringArraySimple';
const isReadonly = node.typeName.name === 'ReadonlyArray';
const readonlyPrefix = isReadonly ? 'readonly ' : '';
const typeParams = node.typeParameters && node.typeParameters.params;
if (!typeParams || typeParams.length === 0) {
// Create an 'any' array
context.report({
node,
messageId,
data: {
type: 'any',
},
fix(fixer) {
return fixer.replaceText(node, `${readonlyPrefix}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,
data: {
type: getMessageType(type),
},
fix(fixer) {
return [
fixer.replaceTextRange([node.range[0], type.range[0]], `${readonlyPrefix}${parens ? '(' : ''}`),
fixer.replaceTextRange([type.range[1], node.range[1]], parens ? ')[]' : '[]'),
];
},
});
},
};
},
});
//# sourceMappingURL=array-type.js.map