Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin)!: [array-type] add "default", "readonly" options #654

Merged
88 changes: 69 additions & 19 deletions packages/eslint-plugin/src/rules/array-type.ts
Expand Up @@ -72,13 +72,21 @@ function typeNeedsParentheses(node: TSESTree.Node): boolean {
}
}

type Options = ['array' | 'generic' | 'array-simple'];
export type ArrayOption = 'array' | 'generic' | 'array-simple';
type Options = [
{
default: ArrayOption;
readonly?: ArrayOption;
}
];
type MessageIds =
| 'errorStringGeneric'
| 'errorStringGenericSimple'
| 'errorStringArray'
| 'errorStringArraySimple';

const arrayOption = { enum: ['array', 'generic', 'array-simple'] };

export default util.createRule<Options, MessageIds>({
name: 'array-type',
meta: {
Expand All @@ -101,14 +109,32 @@ export default util.createRule<Options, MessageIds>({
},
schema: [
{
enum: ['array', 'generic', 'array-simple'],
type: 'object',
properties: {
default: arrayOption,
readonly: arrayOption,
},
},
],
},
defaultOptions: ['array'],
create(context, [option]) {
defaultOptions: [
{
default: 'array',
},
],
create(context, [options]) {
const sourceCode = context.getSourceCode();

const defaultOption = options.default;
const readonlyOption = options.readonly || defaultOption;

const isArraySimpleOption =
defaultOption === 'array-simple' && readonlyOption === 'array-simple';
const isArrayOption =
defaultOption === 'array' && readonlyOption === 'array';
const isGenericOption =
defaultOption === 'generic' && readonlyOption === 'generic';

/**
* Check if whitespace is needed before this node
* @param node the node to be evaluated.
Expand Down Expand Up @@ -142,22 +168,36 @@ export default util.createRule<Options, MessageIds>({
}

return {
TSArrayType(node) {
TSArrayType(node: TSESTree.TSArrayType) {
if (
option === 'array' ||
(option === 'array-simple' && isSimpleType(node.elementType))
isArrayOption ||
(isArraySimpleOption && isSimpleType(node.elementType))
) {
return;
}
const messageId =
option === 'generic'
? 'errorStringGeneric'
: 'errorStringGenericSimple';

const isReadonly =
node.parent &&
node.parent.type === AST_NODE_TYPES.TSTypeOperator &&
node.parent.operator === 'readonly';

const isReadonlyGeneric =
readonlyOption === 'generic' && defaultOption !== 'generic';

const isReadonlyArray =
readonlyOption !== 'generic' && defaultOption === 'generic';

if (
(isReadonlyGeneric && !isReadonly) ||
(isReadonlyArray && isReadonly)
) {
return;
}

const messageId =
defaultOption === 'generic'
? 'errorStringGeneric'
: 'errorStringGenericSimple';
const typeOpNode = isReadonly ? node.parent! : null;

context.report({
Expand Down Expand Up @@ -200,23 +240,32 @@ export default util.createRule<Options, MessageIds>({
},
});
},

TSTypeReference(node: TSESTree.TSTypeReference) {
if (
option === 'generic' ||
isGenericOption ||
node.typeName.type !== AST_NODE_TYPES.Identifier
) {
return;
}
if (!['Array', 'ReadonlyArray'].includes(node.typeName.name)) {

const isReadonlyArrayType = node.typeName.name === 'ReadonlyArray';
const isArrayType = node.typeName.name === 'Array';

if (
!(isArrayType || isReadonlyArrayType) ||
(readonlyOption === 'generic' && isReadonlyArrayType) ||
(defaultOption === 'generic' && !isReadonlyArrayType)
) {
return;
}

const messageId =
option === 'array' ? 'errorStringArray' : 'errorStringArraySimple';
const isReadonly = node.typeName.name === 'ReadonlyArray';
const readonlyPrefix = isReadonly ? 'readonly ' : '';

const readonlyPrefix = isReadonlyArrayType ? 'readonly ' : '';
const typeParams = node.typeParameters && node.typeParameters.params;
const messageId =
defaultOption === 'array'
? 'errorStringArray'
: 'errorStringArraySimple';

if (!typeParams || typeParams.length === 0) {
// Create an 'any' array
Expand All @@ -230,12 +279,13 @@ export default util.createRule<Options, MessageIds>({
return fixer.replaceText(node, `${readonlyPrefix}any[]`);
},
});

return;
}

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