Repository: import-js/eslint-plugin-import (js)

Issue:
no-cycle should accept Flow "typeof" imports, just like "type"
This is an extension of #1098 / #1343. In addition to `import type`, Flow has a syntax `import typeof`. For example ([try-flow link](https://flow.org/try/#0JYWwDg9gTgLgBDAnmAphAZnA3nArgZxQFF10UBjeAXznSghDgHIoUBDSpgbgCgeA3NlFoAuPIRJlKXOAHpZcABZt8CRSjj42IDUlRwVcAErtKAOgLFSFGDyA)):
```js
import typeof { useEffect } from 'react';

var f: useEffect; // has the same type as React.useEffect
```

Like with `import type`, these imports are transpiled away and have no effect at runtime, so they should be given the same treatment as `import type`: that is, they should be ignored.

Currently they're not.

I have a fix which I'll send in a bit.


Pull request:
[Fix] `no-cycle`: Accept `import typeof`, like `import type`
Fixes #2607.

In addition to `import type`, Flow has a syntax `import typeof`. For example ([try-flow link](https://flow.org/try/#0JYWwDg9gTgLgBDAnmAphAZnA3nArgZxQFF10UBjeAXznSghDgHIoUBDSpgbgCgeA3NlFoAuPIRJlKXOAHpZcABZt8CRSjj42IDUlRwVcAErtKAOgLFSFGDyA)):

```js
import typeof { useEffect } from 'react';

var f: useEffect; // has the same type as React.useEffect
```

Like with `import type`, these imports are transpiled away and have no effect at runtime, so they should be given the same treatment as `import type`: that is, they should be ignored.

Currently they're not. This PR fixes that.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66d46653..58bfe0ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 - [`no-unresolved`], [`extensions`]: ignore type only exports ([#2436], thanks [@Lukas-Kullmann])
 - `ExportMap`: add missing param to function ([#2589], thanks [@Fdawgs])
 - [`no-unused-modules`]: `checkPkgFieldObject` filters boolean fields from checks ([#2598], thanks [@mpint])
+- [`no-cycle`]: accept Flow `typeof` imports, just like `type` ([#2608], thanks [@gnprice])
 
 ### Changed
 - [Tests] [`named`]: Run all TypeScript test ([#2427], thanks [@ProdigySim])
@@ -1025,6 +1026,7 @@ for info on changes for earlier releases.
 
 [`memo-parser`]: ./memo-parser/README.md
 
+[#2608]: https://github.com/import-js/eslint-plugin-import/pull/2608
 [#2605]: https://github.com/import-js/eslint-plugin-import/pull/2605
 [#2602]: https://github.com/import-js/eslint-plugin-import/pull/2602
 [#2598]: https://github.com/import-js/eslint-plugin-import/pull/2598
@@ -1626,6 +1628,7 @@ for info on changes for earlier releases.
 [@gavriguy]: https://github.com/gavriguy
 [@georeith]: https://github.com/georeith
 [@giodamelio]: https://github.com/giodamelio
+[@gnprice]: https://github.com/gnprice
 [@golergka]: https://github.com/golergka
 [@golopot]: https://github.com/golopot
 [@GoodForOneFare]: https://github.com/GoodForOneFare
diff --git a/docs/rules/no-cycle.md b/docs/rules/no-cycle.md
index 1593842d..6635ba73 100644
--- a/docs/rules/no-cycle.md
+++ b/docs/rules/no-cycle.md
@@ -22,6 +22,8 @@ import { b } from './dep-b.js' // reported: Dependency cycle detected.
 This rule does _not_ detect imports that resolve directly to the linted module;
 for that, see [`no-self-import`].
 
+This rule ignores type-only imports in Flow and TypeScript syntax (`import type` and `import typeof`), which have no runtime effect.
+
 
 ## Rule Details
 
diff --git a/src/ExportMap.js b/src/ExportMap.js
index a39434bc..d95fdb7a 100644
--- a/src/ExportMap.js
+++ b/src/ExportMap.js
@@ -502,8 +502,8 @@ ExportMap.parse = function (path, content, context) {
   }
 
   function captureDependencyWithSpecifiers(n) {
-    // import type { Foo } (TS and Flow)
-    const declarationIsType = n.importKind === 'type';
+    // import type { Foo } (TS and Flow); import typeof { Foo } (Flow)
+    const declarationIsType = n.importKind === 'type' || n.importKind === 'typeof';
     // import './foo' or import {} from './foo' (both 0 specifiers) is a side effect and
     // shouldn't be considered to be just importing types
     let specifiersOnlyImportingTypes = n.specifiers.length > 0;
@@ -515,8 +515,9 @@ ExportMap.parse = function (path, content, context) {
         importedSpecifiers.add(specifier.type);
       }
 
-      // import { type Foo } (Flow)
-      specifiersOnlyImportingTypes = specifiersOnlyImportingTypes && specifier.importKind === 'type';
+      // import { type Foo } (Flow); import { typeof Foo } (Flow)
+      specifiersOnlyImportingTypes = specifiersOnlyImportingTypes
+        && (specifier.importKind === 'type' || specifier.importKind === 'typeof');
     });
     captureDependency(n, declarationIsType || specifiersOnlyImportingTypes, importedSpecifiers);
   }


Test patch:
diff --git a/tests/files/cycles/flow-typeof.js b/tests/files/cycles/flow-typeof.js
new file mode 100644
index 00000000..7c63f9ab
--- /dev/null
+++ b/tests/files/cycles/flow-typeof.js
@@ -0,0 +1,4 @@
+// @flow
+import typeof Foo from './depth-zero';
+import { typeof Bar } from './depth-zero';
+import typeof { Bar } from './depth-zero';
diff --git a/tests/src/rules/no-cycle.js b/tests/src/rules/no-cycle.js
index de0083f5..155f257b 100644
--- a/tests/src/rules/no-cycle.js
+++ b/tests/src/rules/no-cycle.js
@@ -111,6 +111,10 @@ ruleTester.run('no-cycle', rule, {
       code: 'import { bar } from "./flow-types-only-importing-multiple-types"',
       parser: parsers.BABEL_OLD,
     }),
+    test({
+      code: 'import { bar } from "./flow-typeof"',
+      parser: parsers.BABEL_OLD,
+    }),
   ),
 
   invalid: [].concat(