For AI agents: the complete documentation index is available at https://rslint.rs/llms.txt, the full documentation bundle is available at https://rslint.rs/llms-full.txt, and this page is available as Markdown at https://rslint.rs/rules/import/no-duplicates.md.
close

no-duplicates

Added in v0.4.2

Configuration

PresetConfigured Value
✅ importPlugin.configs.recommended["warn",{"considerQueryString":true}]
rslint.config.ts
import { defineConfig, importPlugin } from '@rslint/core';

export default defineConfig([
  importPlugin.configs.recommended,
  {
    rules: {
      'import/no-duplicates': 'error',
    },
  },
]);

Rule Details

Reports if a resolved path is imported more than once.

This rule is similar to ESLint core's no-duplicate-imports, but differs in two key ways:

  1. The paths in the source code don't have to exactly match — they just have to point to the same module on the filesystem (e.g., ./foo and ./foo.js).
  2. This version distinguishes type imports from standard imports.

Import attributes are part of a module request. Imports with different attributes are not duplicates, while equivalent attribute sets compare equally regardless of property order. Autofix only merges imports whose attributes are equivalent.

Explicit local paths that name existing files use those files as module identities. For example, when both text.js and text.ts exist, imports of ./text.js and ./text.ts are not duplicates, even if TypeScript resolves both to text.ts. The same applies to .jsx/.tsx, .mjs/.mts, and .cjs/.cts pairs.

Package lookup uses runtime targets rather than shared type declarations, retaining import/require conditions and the project's custom conditions. Entry points with different type declarations remain separate even when they share a runtime file. TypeScript path aliases take precedence when a substitution for the selected pattern resolves; missing substitutions allow package lookup to continue.

Queries and fragments selected by package exports remain part of ESM module identity, independently of considerQueryString. CommonJS lookup also resolves relative extensionless requests using Node's runtime extension and directory rules, so adjacent declarations do not hide genuine duplicates.

If the written file does not exist, the rule retains unambiguous TypeScript resolution for aliases and extension substitution. A declaration file alone does not establish runtime identity for a value import. Neither does a result with an existing JavaScript sibling, or one resolved from a different importer directory. In these cases only identical written requests are grouped.

import data from './data.json' with { type: 'json' };
import source from './data.json' with { type: 'text' };

Examples of incorrect code for this rule:

import { x } from './foo';
import { y } from './foo';
import SomeDefaultClass from './mod';
import * as names from './mod';
import { something } from './mod.js';

Examples of correct code for this rule:

import SomeDefaultClass, * as names from './mod';
import type SomeType from './mod';
import { x } from './foo';
import { y } from './bar';

Options

considerQueryString

  • Default:
    • false by default
    • true if using the importPlugin.configs.recommended preset

When set to true, imports with different query strings are treated as different modules.

{
  "import/no-duplicates": ["error", { "considerQueryString": false }]
}

For example, these imports are reported as duplicates when considerQueryString is false, but are allowed when it is true:

import iconUrl from './icon.svg?url';
import iconSource from './icon.svg?raw';

prefer-inline

  • Default: false

When set to true, supports TypeScript inline type imports, allowing import type { X } to be merged into import { type X }.

{
  "import/no-duplicates": ["error", { "prefer-inline": true }]
}

Differences from upstream

When a TypeScript alias resolves to a file with an existing JavaScript sibling, the rule conservatively keeps differently written requests separate. For example, if both text.js and text.ts exist and @files/* maps to ./*, these imports remain separate even if the runtime alias selects the same JavaScript file:

import { x } from '@files/text.js';
import { y } from './text.js';

Original Documentation