[eslint] Consider `useRef() as ...` as constant (#18496)
Sometimes you need to use casts, eg: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/28884#issuecomment-471341041. This change ignores them and allows you to still omit the ref object from the deps list. Test Plan: unit tests
Sophie Alpert committed
Apr 5, 2020 at 14:13 UTC
fe2cb525542443aaf1447c4069354c12659fd186
4 files changed
+136
-13
packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js
+71
-11
@@ -11,14 +11,6 @@ const ESLintTester = require('eslint').RuleTester;
11
const ReactHooksESLintPlugin = require('eslint-plugin-react-hooks');
12
const ReactHooksESLintRule = ReactHooksESLintPlugin.rules['exhaustive-deps'];
13
14
-ESLintTester.setDefaultConfig({
15
- parser: require.resolve('babel-eslint'),
16
- parserOptions: {
17
- ecmaVersion: 6,
18
- sourceType: 'module',
19
- },
20
-});
21
-
14
/**
15
* A string template tag that removes padding from the left side of multi-line strings
16
* @param {Array} strings array of code strings (only one expected)
@@ -6441,11 +6433,65 @@ const tests = {
6433
],
6434
};
6435
6436
+const testsTypescript = {
6437
+ valid: [
6438
+ {
6439
+ // `ref` is still constant, despite the cast.
6440
+ code: normalizeIndent`
6441
+ function MyComponent() {
6442
+ const ref = useRef() as React.MutableRefObject<HTMLDivElement>;
6443
+ useEffect(() => {
6444
+ console.log(ref.current);
6445
+ }, []);
6446
+ }
6447
+ `,
6448
+ },
6449
+ ],
6450
+ invalid: [
6451
+ {
6452
+ // `local` is still non-constant, despite the cast.
6453
+ code: normalizeIndent`
6454
+ function MyComponent() {
6455
+ const local = {} as string;
6456
+ useEffect(() => {
6457
+ console.log(local);
6458
+ }, []);
6459
+ }
6460
+ `,
6461
+ errors: [
6462
+ {
6463
+ message:
6464
+ "React Hook useEffect has a missing dependency: 'local'. " +
6465
+ 'Either include it or remove the dependency array.',
6466
+ suggestions: [
6467
+ {
6468
+ desc: 'Update the dependencies array to be: [local]',
6469
+ output: normalizeIndent`
6470
+ function MyComponent() {
6471
+ const local = {} as string;
6472
+ useEffect(() => {
6473
+ console.log(local);
6474
+ }, [local]);
6475
+ }
6476
+ `,
6477
+ },
6478
+ ],
6479
+ },
6480
+ ],
6481
+ },
6482
+ ],
6483
+};
6484
+
6485
// For easier local testing
6486
if (!process.env.CI) {
6487
let only = [];
6488
let skipped = [];
6448
- [...tests.valid, ...tests.invalid].forEach(t => {
6489
+ [
6490
+ ...tests.valid,
6491
+ ...tests.invalid,
6492
+ ...testsTypescript.valid,
6493
+ ...testsTypescript.invalid,
6494
+ ].forEach(t => {
6495
if (t.skip) {
6496
delete t.skip;
6497
skipped.push(t);
@@ -6466,7 +6512,21 @@ if (!process.env.CI) {
6512
};
6513
tests.valid = tests.valid.filter(predicate);
6514
tests.invalid = tests.invalid.filter(predicate);
6515
+ testsTypescript.valid = testsTypescript.valid.filter(predicate);
6516
+ testsTypescript.invalid = testsTypescript.invalid.filter(predicate);
6517
}
6518
6471
-const eslintTester = new ESLintTester();
6472
-eslintTester.run('react-hooks', ReactHooksESLintRule, tests);
6519
+const parserOptions = {
6520
+ ecmaVersion: 6,
6521
+ sourceType: 'module',
6522
+};
6523
+
6524
+new ESLintTester({
6525
+ parser: require.resolve('babel-eslint'),
6526
+ parserOptions,
6527
+}).run('react-hooks', ReactHooksESLintRule, tests);
6528
+
6529
+new ESLintTester({
6530
+ parser: require.resolve('@typescript-eslint/parser'),
6531
+ parserOptions,
6532
+}).run('react-hooks', ReactHooksESLintRule, testsTypescript);
packages/eslint-plugin-react-hooks/package.json
+4
-1
@@ -25,10 +25,13 @@
25
"url": "https://github.com/facebook/react/issues"
26
},
27
"engines": {
28
- "node": ">=7"
28
+ "node": ">=10"
29
},
30
"homepage": "https://reactjs.org/",
31
"peerDependencies": {
32
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@typescript-eslint/parser": "^2.26.0"
36
}
37
}
packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
+4
-1
@@ -316,10 +316,13 @@ export default {
316
if (def.node.type !== 'VariableDeclarator') {
317
return false;
318
}
319
- const init = def.node.init;
319
+ let init = def.node.init;
320
if (init == null) {
321
return false;
322
}
323
+ while (init.type === 'TSAsExpression') {
324
+ init = init.expression;
325
+ }
326
// Detect primitive constants
327
// const foo = 42
328
let declaration = def.node.parent;
yarn.lock
+57
@@ -1319,6 +1319,11 @@
1319
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
1320
integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
1321
1322
+"@types/eslint-visitor-keys@^1.0.0":
1323
+ version "1.0.0"
1324
+ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
1325
+ integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
1326
+
1327
"@types/estree@*":
1328
version "0.0.42"
1329
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.42.tgz#8d0c1f480339efedb3e46070e22dd63e0430dd11"
@@ -1430,6 +1435,16 @@
1435
dependencies:
1436
"@types/yargs-parser" "*"
1437
1438
+"@typescript-eslint/experimental-utils@2.26.0":
1439
+ version "2.26.0"
1440
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz#063390c404d9980767d76274df386c0aa675d91d"
1441
+ integrity sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ==
1442
+ dependencies:
1443
+ "@types/json-schema" "^7.0.3"
1444
+ "@typescript-eslint/typescript-estree" "2.26.0"
1445
+ eslint-scope "^5.0.0"
1446
+ eslint-utils "^2.0.0"
1447
+
1448
"@typescript-eslint/experimental-utils@^1.13.0":
1449
version "1.13.0"
1450
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e"
@@ -1439,6 +1454,16 @@
1454
"@typescript-eslint/typescript-estree" "1.13.0"
1455
eslint-scope "^4.0.0"
1456
1457
+"@typescript-eslint/parser@^2.26.0":
1458
+ version "2.26.0"
1459
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.26.0.tgz#385463615818b33acb72a25b39c03579df93d76f"
1460
+ integrity sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ==
1461
+ dependencies:
1462
+ "@types/eslint-visitor-keys" "^1.0.0"
1463
+ "@typescript-eslint/experimental-utils" "2.26.0"
1464
+ "@typescript-eslint/typescript-estree" "2.26.0"
1465
+ eslint-visitor-keys "^1.1.0"
1466
+
1467
"@typescript-eslint/typescript-estree@1.13.0":
1468
version "1.13.0"
1469
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e"
@@ -1447,6 +1472,19 @@
1472
lodash.unescape "4.0.1"
1473
semver "5.5.0"
1474
1475
+"@typescript-eslint/typescript-estree@2.26.0":
1476
+ version "2.26.0"
1477
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz#d8132cf1ee8a72234f996519a47d8a9118b57d56"
1478
+ integrity sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg==
1479
+ dependencies:
1480
+ debug "^4.1.1"
1481
+ eslint-visitor-keys "^1.1.0"
1482
+ glob "^7.1.6"
1483
+ is-glob "^4.0.1"
1484
+ lodash "^4.17.15"
1485
+ semver "^6.3.0"
1486
+ tsutils "^3.17.1"
1487
+
1488
"@webassemblyjs/ast@1.8.5":
1489
version "1.8.5"
1490
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
@@ -5169,6 +5207,13 @@ eslint-utils@^1.3.1, eslint-utils@^1.4.3:
5207
dependencies:
5208
eslint-visitor-keys "^1.1.0"
5209
5210
+eslint-utils@^2.0.0:
5211
+ version "2.0.0"
5212
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd"
5213
+ integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==
5214
+ dependencies:
5215
+ eslint-visitor-keys "^1.1.0"
5216
+
5217
eslint-visitor-keys@1.0.0:
5218
version "1.0.0"
5219
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
@@ -13210,6 +13255,18 @@ tslib@^1, tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3:
13255
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
13256
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
13257
13258
+tslib@^1.8.1:
13259
+ version "1.11.1"
13260
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
13261
+ integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
13262
+
13263
+tsutils@^3.17.1:
13264
+ version "3.17.1"
13265
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
13266
+ integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==
13267
+ dependencies:
13268
+ tslib "^1.8.1"
13269
+
13270
tty-browserify@0.0.0:
13271
version "0.0.0"
13272
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"