Skip to content

Commit 1389393

Browse files
a-tarasyukbradzacher
authored andcommitted
feat(eslint-plugin)!: [array-type] rework options (#654)
BREAKING CHANGE: changes config structure ```ts type ArrayOption = 'array' | 'generic' | 'array-simple'; type Options = [ { // default case for all arrays default: ArrayOption, // optional override for readonly arrays readonly?: ArrayOption, }, ]; ``` Fixes #635
1 parent ef99f71 commit 1389393

File tree

3 files changed

+262
-114
lines changed

3 files changed

+262
-114
lines changed

packages/eslint-plugin/docs/rules/array-type.md

+18-5
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@ This rule aims to standardise usage of array types within your codebase.
88

99
## Options
1010

11-
This rule accepts one option - a single string
11+
```ts
12+
type ArrayOption = 'array' | 'generic' | 'array-simple';
13+
type Options = {
14+
default: ArrayOption;
15+
readonly?: ArrayOption;
16+
};
17+
18+
const defaultOptions: Options = {
19+
default: 'array',
20+
};
21+
```
22+
23+
The rule accepts an options object with the following properties:
24+
25+
- `default` - sets the array type expected for mutable cases.
26+
- `readonly` - sets the array type expected for readonly arrays. If this is omitted, then the value for `default` will be used.
1227

13-
- `"array"` enforces use of `T[]` for all types `T`.
14-
- `"generic"` enforces use of `Array<T>` for all types `T`.
15-
- `"array-simple"` enforces use of `T[]` if `T` is a simple type.
28+
Each property can be set to one of three strings: `'array' | 'generic' | 'array-simple'`.
1629

17-
Without providing an option, by default the rule will enforce `"array"`.
30+
The default config will enforce that all mutable and readonly arrays use the `'array'` syntax.
1831

1932
### `"array"`
2033

packages/eslint-plugin/src/rules/array-type.ts

+68-19
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,20 @@ function typeNeedsParentheses(node: TSESTree.Node): boolean {
7373
}
7474

7575
export type OptionString = 'array' | 'generic' | 'array-simple';
76-
type Options = [OptionString];
76+
type Options = [
77+
{
78+
default: OptionString;
79+
readonly?: OptionString;
80+
}
81+
];
7782
type MessageIds =
7883
| 'errorStringGeneric'
7984
| 'errorStringGenericSimple'
8085
| 'errorStringArray'
8186
| 'errorStringArraySimple';
8287

88+
const arrayOption = { enum: ['array', 'generic', 'array-simple'] };
89+
8390
export default util.createRule<Options, MessageIds>({
8491
name: 'array-type',
8592
meta: {
@@ -102,14 +109,32 @@ export default util.createRule<Options, MessageIds>({
102109
},
103110
schema: [
104111
{
105-
enum: ['array', 'generic', 'array-simple'],
112+
type: 'object',
113+
properties: {
114+
default: arrayOption,
115+
readonly: arrayOption,
116+
},
106117
},
107118
],
108119
},
109-
defaultOptions: ['array'],
110-
create(context, [option]) {
120+
defaultOptions: [
121+
{
122+
default: 'array',
123+
},
124+
],
125+
create(context, [options]) {
111126
const sourceCode = context.getSourceCode();
112127

128+
const defaultOption = options.default;
129+
const readonlyOption = options.readonly || defaultOption;
130+
131+
const isArraySimpleOption =
132+
defaultOption === 'array-simple' && readonlyOption === 'array-simple';
133+
const isArrayOption =
134+
defaultOption === 'array' && readonlyOption === 'array';
135+
const isGenericOption =
136+
defaultOption === 'generic' && readonlyOption === 'generic';
137+
113138
/**
114139
* Check if whitespace is needed before this node
115140
* @param node the node to be evaluated.
@@ -143,22 +168,36 @@ export default util.createRule<Options, MessageIds>({
143168
}
144169

145170
return {
146-
TSArrayType(node) {
171+
TSArrayType(node: TSESTree.TSArrayType) {
147172
if (
148-
option === 'array' ||
149-
(option === 'array-simple' && isSimpleType(node.elementType))
173+
isArrayOption ||
174+
(isArraySimpleOption && isSimpleType(node.elementType))
150175
) {
151176
return;
152177
}
153-
const messageId =
154-
option === 'generic'
155-
? 'errorStringGeneric'
156-
: 'errorStringGenericSimple';
157178

158179
const isReadonly =
159180
node.parent &&
160181
node.parent.type === AST_NODE_TYPES.TSTypeOperator &&
161182
node.parent.operator === 'readonly';
183+
184+
const isReadonlyGeneric =
185+
readonlyOption === 'generic' && defaultOption !== 'generic';
186+
187+
const isReadonlyArray =
188+
readonlyOption !== 'generic' && defaultOption === 'generic';
189+
190+
if (
191+
(isReadonlyGeneric && !isReadonly) ||
192+
(isReadonlyArray && isReadonly)
193+
) {
194+
return;
195+
}
196+
197+
const messageId =
198+
defaultOption === 'generic'
199+
? 'errorStringGeneric'
200+
: 'errorStringGenericSimple';
162201
const typeOpNode = isReadonly ? node.parent! : null;
163202

164203
context.report({
@@ -201,23 +240,32 @@ export default util.createRule<Options, MessageIds>({
201240
},
202241
});
203242
},
243+
204244
TSTypeReference(node: TSESTree.TSTypeReference) {
205245
if (
206-
option === 'generic' ||
246+
isGenericOption ||
207247
node.typeName.type !== AST_NODE_TYPES.Identifier
208248
) {
209249
return;
210250
}
211-
if (!['Array', 'ReadonlyArray'].includes(node.typeName.name)) {
251+
252+
const isReadonlyArrayType = node.typeName.name === 'ReadonlyArray';
253+
const isArrayType = node.typeName.name === 'Array';
254+
255+
if (
256+
!(isArrayType || isReadonlyArrayType) ||
257+
(readonlyOption === 'generic' && isReadonlyArrayType) ||
258+
(defaultOption === 'generic' && !isReadonlyArrayType)
259+
) {
212260
return;
213261
}
214262

215-
const messageId =
216-
option === 'array' ? 'errorStringArray' : 'errorStringArraySimple';
217-
const isReadonly = node.typeName.name === 'ReadonlyArray';
218-
const readonlyPrefix = isReadonly ? 'readonly ' : '';
219-
263+
const readonlyPrefix = isReadonlyArrayType ? 'readonly ' : '';
220264
const typeParams = node.typeParameters && node.typeParameters.params;
265+
const messageId =
266+
defaultOption === 'array'
267+
? 'errorStringArray'
268+
: 'errorStringArraySimple';
221269

222270
if (!typeParams || typeParams.length === 0) {
223271
// Create an 'any' array
@@ -231,12 +279,13 @@ export default util.createRule<Options, MessageIds>({
231279
return fixer.replaceText(node, `${readonlyPrefix}any[]`);
232280
},
233281
});
282+
234283
return;
235284
}
236285

237286
if (
238287
typeParams.length !== 1 ||
239-
(option === 'array-simple' && !isSimpleType(typeParams[0]))
288+
(defaultOption === 'array-simple' && !isSimpleType(typeParams[0]))
240289
) {
241290
return;
242291
}

0 commit comments

Comments
 (0)