Skip to content

Commit 7c57cea

Browse files
pnevykgajus
authored andcommitted
feat: don't require $ReadOnlyArray if variable is initialized with empty array (#337)
* feat: Don't require $ReadOnlyArray if variable is initialized with empty array * docs: phrasing
1 parent ff4f857 commit 7c57cea

File tree

3 files changed

+66
-10
lines changed

3 files changed

+66
-10
lines changed

Diff for: .README/rules/no-mutable-array.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ General reasons for using immutable data structures:
1313
* They always have failure atomicity
1414
* They are much easier to cache
1515

16+
Note that initialization of a variable with an empty array is considered valid (e.g., `const values: Array<string> = [];`). This behavior resembles the behavior of Flow's [unsealed objects](https://flow.org/en/docs/types/objects/#toc-unsealed-objects), as it is assumed that empty array is intended to be mutated.
17+
1618
<!-- assertions noMutableArray -->

Diff for: src/rules/noMutableArray.js

+39-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,49 @@
1+
import _ from 'lodash';
2+
13
const schema = [];
24

5+
// const x = [];
6+
const isEmptyArrayLiteral = (node) => {
7+
return _.get(node, 'init.type') === 'ArrayExpression' && _.get(node, 'init.elements.length') === 0;
8+
};
9+
10+
// const x = new Array(); const y = Array();
11+
const isEmptyArrayInstance = (node) => {
12+
if (_.get(node, 'init.type') === 'NewExpression' || _.get(node, 'init.type') === 'CallExpression') {
13+
return _.get(node, 'init.callee.name') === 'Array' && _.get(node, 'init.arguments.length') === 0;
14+
} else {
15+
return false;
16+
}
17+
};
18+
19+
const isAnnotationOfEmptyArrayInit = (node) => {
20+
if (_.has(node, 'parent.parent.parent')) {
21+
const parent = _.get(node, 'parent.parent.parent');
22+
const isVariableDeclaration = _.get(parent, 'type') === 'VariableDeclarator';
23+
24+
return isVariableDeclaration && (isEmptyArrayLiteral(parent) || isEmptyArrayInstance(parent));
25+
} else {
26+
return false;
27+
}
28+
};
29+
330
const create = (context) => {
431
return {
532
ArrayTypeAnnotation (node) {
6-
context.report({
7-
fix (fixer) {
8-
const rawElementType = context.getSourceCode().getText(node.elementType);
9-
10-
return fixer.replaceText(node, '$ReadOnlyArray<' + rawElementType + '>');
11-
},
12-
message: 'Use "$ReadOnlyArray" instead of array shorthand notation',
13-
node
14-
});
33+
if (!isAnnotationOfEmptyArrayInit(node)) {
34+
context.report({
35+
fix (fixer) {
36+
const rawElementType = context.getSourceCode().getText(node.elementType);
37+
38+
return fixer.replaceText(node, '$ReadOnlyArray<' + rawElementType + '>');
39+
},
40+
message: 'Use "$ReadOnlyArray" instead of array shorthand notation',
41+
node
42+
});
43+
}
1544
},
1645
GenericTypeAnnotation (node) {
17-
if (node.id.name === 'Array') {
46+
if (node.id.name === 'Array' && !isAnnotationOfEmptyArrayInit(node)) {
1847
context.report({
1948
fix (fixer) {
2049
return fixer.replaceText(node.id, '$ReadOnlyArray');

Diff for: tests/rules/assertions/noMutableArray.js

+25
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,36 @@ export default {
99
code: 'type X = string[]',
1010
errors: [{message: 'Use "$ReadOnlyArray" instead of array shorthand notation'}],
1111
output: 'type X = $ReadOnlyArray<string>'
12+
},
13+
{
14+
code: 'const values: Array<Array<string>> = [];',
15+
errors: [{message: 'Use "$ReadOnlyArray" instead of "Array"'}],
16+
output: 'const values: Array<$ReadOnlyArray<string>> = [];'
17+
},
18+
{
19+
code: 'let values: Array<Array<string>>;',
20+
errors: [
21+
{message: 'Use "$ReadOnlyArray" instead of "Array"'},
22+
{message: 'Use "$ReadOnlyArray" instead of "Array"'}
23+
],
24+
output: 'let values: $ReadOnlyArray<$ReadOnlyArray<string>>;'
1225
}
1326
],
1427
valid: [
1528
{
1629
code: 'type X = $ReadOnlyArray<string>'
30+
},
31+
{
32+
code: 'const values: Array<$ReadOnlyArray<string>> = [];'
33+
},
34+
{
35+
code: 'const values: $ReadOnlyArray<string>[] = [];'
36+
},
37+
{
38+
code: 'const values: Array<$ReadOnlyArray<string>> = new Array();'
39+
},
40+
{
41+
code: 'const values: Array<$ReadOnlyArray<string>> = Array();'
1742
}
1843
]
1944
};

0 commit comments

Comments
 (0)