Skip to content

noImportCycles

Prevent import cycles.

This rule warns when a file imports another file that, either directly or indirectly, imports the original file again.

Cycles can lead to symbols that are unexpectedly undefined and are generally considered poor code hygiene.

If a cycle is detected, it is advised to move code such that imports only go in a single direction, i.e. they don’t point “back” to the importing file.

foobar.js

import { baz } from "./baz.js";
export function foo() {
baz();
}
export function bar() {
console.log("foobar");
}

baz.js

import { bar } from "./foobar.js";
export function baz() {
bar();
}

foo.js

import { baz } from "./baz.js";
export function foo() {
baz();
}

bar.js

export function bar() {
console.log("foobar");
}

baz.js

import { bar } from "./bar.js";
export function baz() {
bar();
}

types.ts

import type { bar } from "./qux.ts";
export type Foo = {
bar: typeof bar;
};

qux.ts

import type { Foo } from "./types.ts";
export function bar(foo: Foo) {
console.log(foo);
}

The rule provides the options described below.

Ignores type-only imports when finding an import cycle. A type-only import (import type) will be removed by the compiler, so it cuts an import cycle at runtime. Note that named type imports (import { type Foo }) aren’t considered as type-only because it’s not removed by the compiler if the verbatimModuleSyntax option is enabled. Enabled by default.

{
"options": {
"ignoreTypes": false
}
}

types.ts

import type { bar } from "./qux.ts";
export type Foo = {
bar: typeof bar;
};

qux.ts

import type { Foo } from "./types.ts";
export function bar(foo: Foo) {
console.log(foo);
}
biome.json
{
"linter": {
"rules": {
"nursery": {
"noImportCycles": "error"
}
}
}
}