@samitouri / QOS-React-2 / commits / fe2cb52554

[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"