Repository: import-js/eslint-plugin-import
Pull request #2220 (2021-09-07 14:58:46)

[Fix] no-unresolved: ignore type-only imports
I'm getting the following errors when linting TypeScript, without installing eslint-import-resolver-typescript:
```
  1:24  error  Unable to resolve path to module 'mdast'  import/no-unresolved
  3:24  error  Unable to resolve path to module 'unist'  import/no-unresolved
```

Can `import/no-unresolved: off` be added to the `plugin:import/typescript` preset, by the same logic as in #1726 (TypeScript compilation already confirms that imports are resolved)?


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26a6e76f..dbb9ecb8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,15 +6,19 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 
 ## [Unreleased]
 
-### Changed
-- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], [@mgwalker])
-
 ### Added
 - [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev])
 - [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser])
 - [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho])
 - [`no-unused-modules`]: support dynamic imports ([#1660], [#2212], thanks [@maxkomarychev], [@aladdin-add], [@Hypnosphi])
 
+### Fixed
+- [`no-unresolved`]: ignore type-only imports ([#2220], thanks [@jablko])
+
+### Changed
+- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker])
+- [patch] TypeScript config: remove `.d.ts` from [`import/parsers` setting] and [`import/extensions` setting] ([#2220], thanks [@jablko])
+
 ## [2.24.2] - 2021-08-24
 
 ### Fixed
@@ -230,7 +234,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 - [`order`]: add option pathGroupsExcludedImportTypes to allow ordering of external import types ([#1565], thanks [@Mairu])
 
 ### Fixed
-- [`no-unused-modules`]: fix usage of `import/extensions` settings ([#1560], thanks [@stekycz])
+- [`no-unused-modules`]: fix usage of [`import/extensions` setting] ([#1560], thanks [@stekycz])
 - [`extensions`]: ignore non-main modules ([#1563], thanks [@saschanaz])
 - TypeScript config: lookup for external modules in @types folder ([#1526], thanks [@joaovieira])
 - [`no-extraneous-dependencies`]: ensure `node.source` is truthy ([#1589], thanks [@ljharb])
@@ -909,6 +913,7 @@ for info on changes for earlier releases.
 
 [`memo-parser`]: ./memo-parser/README.md
 
+[#2220]: https://github.com/import-js/eslint-plugin-import/pull/2220
 [#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219
 [#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212
 [#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196
@@ -1444,6 +1449,7 @@ for info on changes for earlier releases.
 [@isiahmeadows]: https://github.com/isiahmeadows
 [@IvanGoncharov]: https://github.com/IvanGoncharov
 [@ivo-stefchev]: https://github.com/ivo-stefchev
+[@jablko]: https://github.com/jablko
 [@jakubsta]: https://github.com/jakubsta
 [@jeffshaver]: https://github.com/jeffshaver
 [@jf248]: https://github.com/jf248
diff --git a/config/typescript.js b/config/typescript.js
index 01b59f06..ed03fb3f 100644
--- a/config/typescript.js
+++ b/config/typescript.js
@@ -2,7 +2,10 @@
  * Adds `.jsx`, `.ts` and `.tsx` as an extension, and enables JSX/TSX parsing.
  */
 
-const allExtensions = ['.ts', '.tsx', '.d.ts', '.js', '.jsx'];
+// Omit `.d.ts` because 1) TypeScript compilation already confirms that
+// types are resolved, and 2) it would mask an unresolved
+// `.ts`/`.tsx`/`.js`/`.jsx` implementation.
+const allExtensions = ['.ts', '.tsx', '.js', '.jsx'];
 
 module.exports = {
 
@@ -10,7 +13,7 @@ module.exports = {
     'import/extensions': allExtensions,
     'import/external-module-folders': ['node_modules', 'node_modules/@types'],
     'import/parsers': {
-      '@typescript-eslint/parser': ['.ts', '.tsx', '.d.ts'],
+      '@typescript-eslint/parser': ['.ts', '.tsx'],
     },
     'import/resolver': {
       'node': {
diff --git a/src/rules/no-unresolved.js b/src/rules/no-unresolved.js
index d7212560..408b6ff5 100644
--- a/src/rules/no-unresolved.js
+++ b/src/rules/no-unresolved.js
@@ -27,6 +27,11 @@ module.exports = {
     const options = context.options[0] || {};
 
     function checkSourceValue(source) {
+      // ignore type-only imports
+      if (source.parent && source.parent.importKind === 'type') {
+        return;
+      }
+
       const caseSensitive = !CASE_SENSITIVE_FS && options.caseSensitive !== false;
       const caseSensitiveStrict = !CASE_SENSITIVE_FS && options.caseSensitiveStrict;
 


Test patch:
diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js
index 19203074..9e6db8c0 100644
--- a/tests/src/rules/no-unresolved.js
+++ b/tests/src/rules/no-unresolved.js
@@ -1,6 +1,6 @@
 import * as path from 'path';
 
-import { test, SYNTAX_CASES, testVersion } from '../utils';
+import { getTSParsers, test, SYNTAX_CASES, testVersion } from '../utils';
 
 import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve';
 
@@ -441,3 +441,23 @@ ruleTester.run('import() with built-in parser', rule, {
     })) || [],
   ),
 });
+
+context('TypeScript', () => {
+  getTSParsers().filter(x => x !== require.resolve('typescript-eslint-parser')).forEach((parser) => {
+    ruleTester.run(`${parser}: no-unresolved ignore type-only`, rule, {
+      valid: [
+        test({
+          code: 'import type { JSONSchema7Type } from "@types/json-schema";',
+          parser,
+        }),
+      ],
+      invalid: [
+        test({
+          code: 'import { JSONSchema7Type } from "@types/json-schema";',
+          errors: [ "Unable to resolve path to module '@types/json-schema'." ],
+          parser,
+        }),
+      ],
+    });
+  });
+});