# eslint-import-resolver-typescript: teaching ESLint where TypeScript imports live

> eslint-import-resolver-typescript is a resolver, not an ESLint plugin, that gives eslint-plugin-import and eslint-plugin-import-x the ability to follow TypeScript module resolution, including tsconfig paths, .ts and .tsx extensions and @types packages. It fits any TypeScript codebase whose import rules currently report unresolved modules that TypeScript itself resolves fine.

**import-js/eslint-import-resolver-typescript** — This resolver adds `TypeScript` support to `eslint-plugin-import(-x)`

- Repository: https://github.com/import-js/eslint-import-resolver-typescript
- Website: https://npm.im/eslint-import-resolver-typescript
- Stars: 829 · Forks: 72
- Language: TypeScript
- License: ISC
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/import-js-eslint-import-resolver-typescript

## A resolver, not a plugin, and why the difference matters

The README opens with a clarification that saves an hour of confusion: this is a resolver for the `eslint-plugin-import(-x)` plugin, not an ESLint plugin itself. It adds TypeScript support to eslint-plugin-import. The README also points out that if you want speed, `eslint-plugin-import-x` may be the plugin to try.

That distinction is the whole architecture. ESLint does not resolve imports itself. It asks a resolver, which is a small module with a contract, and the import plugin runs its rules, such as import/no-unresolved, against whatever the resolver reports. TypeScript's module resolution is different from Node's. It honours the `paths` mapping in tsconfig.json, it understands the `.ts`, `.tsx`, `.cts`, `.mts` and their declaration variants, it can prefer `@types` packages, and it reads the `imports` and `exports` fields in package.json. A Node resolver does none of that.

The result is the familiar failure mode. TypeScript compiles, the type checker is happy, and the editor resolves the import. ESLint reports the import as unresolved, because the tool doing the checking has no idea how the project is configured. This resolver closes that gap by handing ESLint the same answers the compiler gives.

The audience is therefore narrow and obvious: teams running import rules on a TypeScript codebase, especially monorepos where tsconfig paths do the heavy lifting. If your project is plain JavaScript, this package solves a problem you do not have.

## What the resolver now understands

The README lists what becomes possible once the resolver is in place, and the list maps directly onto TypeScript's resolution features.

First, files can be imported or required with the extension `.cts`, `.mts`, `.ts`, `.tsx`, `.d.cts`, `.d.mts` or `.d.ts`. That matters because the default Node resolver looks for `.js`, `.json` and `.node`, and TypeScript projects routinely import a `.ts` file by its extension or by extensionless path.

Second, the `paths` defined in `tsconfig.json` are used. This is the big one for monorepos. A path mapping such as an alias for a shared workspace package is a compiler feature, and without resolver support every import through that alias is reported as unresolved even though it compiles and runs.

Third, `@types/*` definitions are preferred over plain `.js` or `.jsx`. That behaviour follows from the notice section, which says that after version 2.0.0, `.d.ts` takes higher priority than normal `.js` or `.jsx` files when resolving `node_modules` packages, in favour of `@types/*` definitions or their own definition. In practice this is what makes import/named work on a typed package, because the named exports are known from the declaration file rather than guessed from the JavaScript.

Fourth, multiple tsconfigs are supported, just like normal, which covers projects where different directories have different compiler settings. Fifth, the `imports` and `exports` fields in `package.json` are supported, covering the package entry point mapping that newer Node and bundler ecosystems rely on.

One more option exists for the type resolution edge case. The `alwaysTryTypes` setting makes the resolver try to resolve types under the `<root>@types` directory even when it contains no source code, with `@types/unist` given as the example. That is for the packages that ship only declarations, where a rule would otherwise have nothing to inspect.

## Installing the resolver next to the import plugin

Installation is a two package operation, because the resolver is useless without the plugin that consumes it. The README gives the commands for npm, pnpm, yarn and bun, for both plugin choices.

For eslint-plugin-import-x:

```sh
# npm
npm i -D eslint-plugin-import-x eslint-import-resolver-typescript
```

For eslint-plugin-import the command differs only in the plugin name:

```sh
# npm
npm i -D eslint-plugin-import eslint-import-resolver-typescript
```

The README also lists the pnpm, yarn and bun equivalents for each case, which matters because the resolver appears in the import plugin configuration by module name, and the resolver is loaded by whatever module resolution the flat config file itself uses. A project running Bun, or one with an unusual `exports` map in its own package.json, can hit resolution problems in the config file that have nothing to do with the resolver's logic.

That is also why the package publishes with the dual entry points visible in its manifest. The package is ESM with a `type` of `module`, and it exposes `main` as `lib/index.cjs`, `module` as `lib/index.js` and `types` as `lib/index.d.cts`, with an `exports` map that provides separate `import` and `require` conditions, each with its own types entry. The `require` branch points at `./lib/index.d.cts` and the `import` branch at `./lib/index.d.ts`, so both module systems get the right declaration file. In a monorepo where the config file is CommonJS and the plugin is ESM, or the reverse, that pairing is what keeps the types intact.

The peer dependencies are declared as `eslint`, `eslint-plugin-import` and `eslint-plugin-import-x`, each at `*`, so the package does not force a version on the plugin, and the resolver works with either plugin, or with both installed in different projects.

## Two flat config shapes and the version that enables the new one

Configuration is where this package changed recently, and the README is explicit about which shape applies to whom.

If you are using `eslint-plugin-import-x@>=4.5.0`, you can reference the resolver directly in your flat config with an import or require statement:

```js
// eslint.config.js (CommonJS is also supported)
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
```

and then reference it from the settings:

```js
      'import-x/resolver-next': [
        createTypeScriptImportResolver({
          alwaysTryTypes: true, // Always try to resolve types under `<root>@types` directory even if it doesn't contain any source code, like `@types/unist`
```

The `resolver-next` setting and the factory function are the newer path. The README states the restriction directly: if you are using eslint-plugin-import or the older version of eslint-plugin-import-x, you cannot use require or import, and the resolver must be referenced by name as a string:

```js
      'import/resolver': {
        typescript: {
          alwaysTryTypes: true, // Always try to resolve types under `<root>@types` directory even if it doesn't contain any source code, like `@types/unist`
```

The distinction matters because the two shapes are not interchangeable, and picking the wrong one produces a configuration that either does not load or silently never calls the resolver. The rule of thumb is simple. Newer import-x, use the factory. The original plugin, or an older import-x, use the string name under `import/resolver`.

The factory approach has an obvious advantage beyond style. Because the options are passed as a real object, they are type checked against the resolver's own types, so a misspelled option key is a type error rather than a silently ignored setting.

## The project option and the monorepo layouts

The most consequential option is `project`, because it decides which tsconfig the resolver consults. The README enumerates the shapes.

Omit it and the resolver uses `<root>/tsconfig.json` or `<root>/jsconfig.json` by default. Point it at a folder and it uses that folder's config:

```js
          project: 'path/to/folder',
```

For a monorepo, the README offers an array of explicit paths:

```js
          project: [
            'packages/module-a/tsconfig.json',
            'packages/module-b/jsconfig.json',
          ],
```

or a glob pattern, either a single string:

```js
          project: 'packages/*/{ts,js}config.json',
```

or an array of glob patterns, for the case where different package families use different config layouts.

The README attaches a warning to the multiple tsconfig route: it is useful for monorepos, but is discouraged in favour of `references` supported by TypeScript itself. That is the important design guidance in this section. If your monorepo already uses project references, a single tsconfig with references describes the whole build, and pointing the resolver at one config is both faster and less ambiguous than handing it a glob of every package config. The array form is the fallback for repositories that have not adopted references.

There is one more environment-specific flag. The `bun: true` option resolves Bun modules, and the README has a dedicated section for Bun, which is reasonable since Bun's module semantics differ enough from Node's to need their own handling in a resolver.

## Options inherited from unrs-resolver

Beyond the TypeScript specific options, the resolver passes through a set of options that come from `unrs-resolver`. The README lists them by name: `conditionNames`, `extensions`, `extensionAlias` and `mainFields`, plus a section for other options and a section for default options.

These are the knobs that control the non-TypeScript half of resolution. `conditionNames` decides which export conditions are honoured, which is how a package with separate browser and node builds resolves differently depending on the environment. `extensions` is the list of extensions tried when a specifier has none, and this is where the TypeScript extensions would otherwise have to be added by hand. `extensionAlias` handles the case where one extension is an alias for another. `mainFields` selects which fields of a package.json are consulted for the entry point, in a defined priority order.

The fact that these options are inherited rather than reimplemented is the point. The resolver does not contain its own module resolution algorithm, it configures an existing one and adds the TypeScript layer on top. That keeps the behavioural surface of the resolver aligned with the wider JavaScript tooling ecosystem instead of drifting away from it.

It also sets an expectation. If you are debugging a resolution failure and the answer is not in the TypeScript options, look in the unrs-resolver options, because that is where the generic behaviour lives.

## Node support, ESM exports and where the releases stand

The package manifest states the runtime requirement: Node `^16.17.0 || >=18.6.0`. That excludes Node 14 and 16.0 through 16.16, and it means the resolver can rely on features and resolutions that older Node versions lack.

The build and test setup explains how the ESM and CommonJS entry points are produced. The `build` script runs the TypeScript compiler and tsdown, with tsdown producing the CommonJS bundle from `src/index.ts` into `lib`. The `test` script runs an end to end Node test from `tests/e2e/withJsExtension/test.cjs` and then the Vitest suite, and there is a separate `lint:tsc` script that type checks the project without emitting. A `typecov` script runs type coverage, which is tracked by the badge in the README, so the project's own type coverage is treated as a quality gate rather than a nicety.

The repository layout shows a project with several forms of automation: changesets for versioning and changelog generation, a size-limit configuration to keep the published bundle small, Prettier for formatting with a matching ignore file, remark configuration for documentation linting, Renovate for dependency updates, simple-git-hooks with a nano-staged configuration for commit workflow, and a `.nvmrc` pinning the Node version used in development. The vitest configuration and setup file, the tsconfig split between the library and the project, and the eslint config at the root complete the picture.

On releases, the history shows v4.4.3 on 2025-06-05, v4.4.4 on 2025-06-25 and v4.4.5 on 2026-06-01. The last push was on 2026-06-02, one day after that release, and the repository is not archived. The gap between v4.4.4 and v4.4.5 was almost a year, so the cadence is irregular rather than continuous.

The licence is ISC, a permissive grant with no copyleft requirement, and the README also links a changelog, a contributing guide and a funding page on Open Collective. One practical warning belongs here too. The README states that if you face problems with the rules `import/default` or `import/named`, you should not report them against this resolver, because those rules are working as expected on its side, and asks you to post the issue against eslint-plugin-import instead. That is the correct triage, and it saves a maintainer from debugging a rule that is not theirs.

## Conclusion

Adopt eslint-import-resolver-typescript when your TypeScript codebase already runs import rules that fail only on module resolution, since the resolver aligns ESLint with the compiler instead of duplicating its logic. Do not adopt it if your project is plain JavaScript, where the default resolver is enough, and check your Node version first because the package manifest requires Node 16.17 or 18.6 and above. Verify first which of the two configuration shapes applies to you, since the import-x resolver-next setting needs eslint-plugin-import-x at 4.5.0 or above while the import/resolver setting is for the older plugin, and then confirm that your tsconfig is reachable, because the default is to use <root>/tsconfig.json or <root>/jsconfig.json only when no project option is given.

## FAQ

### How do I install eslint-import-resolver-typescript?

Install it as a dev dependency alongside the import plugin you use, for example npm i -D eslint-plugin-import-x eslint-import-resolver-typescript or npm i -D eslint-plugin-import eslint-import-resolver-typescript, with equivalent pnpm, yarn and bun commands in the README. The peer dependencies are eslint, eslint-plugin-import and eslint-plugin-import-x.

### Why does eslint-import-resolver-typescript prefer .d.ts over .js files?

The README states that after version 2.0.0, .d.ts takes higher priority than normal .js or .jsx files when resolving node_modules packages, in favor of @types/* definitions or the package's own definition. This is what allows rules such as import/named to see the declared exports of a typed package.

### How do I configure the resolver in eslint.config.js?

With eslint-plugin-import-x at 4.5.0 or above you can import createTypeScriptImportResolver from eslint-import-resolver-typescript and register it under the import-x/resolver-next setting. With eslint-plugin-import or an older import-x you cannot use require or import, and you register the resolver by name under import/resolver with a typescript key.

### How does the project option work in a monorepo?

The project option accepts a folder, a glob pattern, an array of paths, or an array of globs, and omitting it falls back to <root>/tsconfig.json or <root>/jsconfig.json. The README notes that multiple tsconfigs are useful for monorepos but are discouraged in favor of TypeScript references.

### What Node version does eslint-import-resolver-typescript require?

The package manifest declares engines as node ^16.17.0 || >=18.6.0. The current version is 4.4.5, released on 2026-06-01, and the repository is licensed under ISC.

## Sources

- [import-js/eslint-import-resolver-typescript on GitHub](https://github.com/import-js/eslint-import-resolver-typescript)
- [License: ISC](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/LICENSE)
- [Project website](https://npm.im/eslint-import-resolver-typescript)
- [README](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/README.md)
- [Releases](https://github.com/import-js/eslint-import-resolver-typescript/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/import-js-eslint-import-resolver-typescript
