Identifies and helps resolve circular import errors in TypeScript/JavaScript projects to prevent runtime failures.
Install
mkdir -p .claude/skills/typescript-circular-dependency && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5306" && unzip -o skill.zip -d .claude/skills/typescript-circular-dependency && rm skill.zipInstalls to .claude/skills/typescript-circular-dependency
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Detect and resolve TypeScript/JavaScript circular import dependencies. Use when:
(1) "Cannot access 'X' before initialization" at runtime, (2) Import returns
undefined unexpectedly, (3) "ReferenceError: Cannot access X before initialization",
(4) Type errors that disappear when you change import order, (5) Jest/Vitest tests
fail with undefined imports that work in browser.Key capabilities
- →Detect circular module imports in TypeScript projects
- →Visualize dependency graphs using image generation
- →Provide resolution strategies for circular dependencies
- →Configure CI/build processes to prevent future cycles
- →Integrate linting rules for cycle detection
How it works
The skill identifies circular dependencies by analyzing module import paths and provides specific refactoring strategies such as dependency injection, dynamic imports, or extracting shared interfaces to break the cycle.
Inputs & outputs
When to use typescript-circular-dependency
- →Fix ReferenceError at runtime
- →Detect circular module imports
- →Visualize dependency graph
- →Resolve import initialization issues
About this skill
TypeScript Circular Dependency Detection and Resolution
Problem
Circular dependencies occur when module A imports from module B, which imports
(directly or indirectly) from module A. TypeScript compiles successfully, but at
runtime, one of the imports evaluates to undefined because the module hasn't
finished initializing yet.
Context / Trigger Conditions
Common error messages:
ReferenceError: Cannot access 'UserService' before initialization
TypeError: Cannot read properties of undefined (reading 'create')
TypeError: (0 , _service.doSomething) is not a function
Symptoms that suggest circular imports:
- Import is
undefinedeven though the export exists - Error only appears at runtime, not during TypeScript compilation
- Moving an import statement changes which import is undefined
- Tests fail but the app works (or vice versa)
- Adding
console.logat the top of a file changes behavior
Solution
Step 1: Detect the Cycle
Use a tool to visualize dependencies:
# Install madge
npm install -g madge
# Find circular dependencies
madge --circular --extensions ts,tsx src/
# Generate visual graph
madge --circular --image graph.svg src/
Or use the TypeScript compiler:
# Check for cycles (requires tsconfig setting)
npx tsc --listFiles | head -50
Step 2: Identify the Pattern
Common circular dependency patterns:
Pattern A: Service-to-Service
services/userService.ts → services/orderService.ts → services/userService.ts
Pattern B: Type imports
types/user.ts → types/order.ts → types/user.ts
Pattern C: Index barrel files
components/index.ts → components/Button.tsx → components/index.ts
Step 3: Resolution Strategies
Strategy 1: Extract Shared Dependencies
Before:
// userService.ts
import { OrderService } from './orderService';
export class UserService { ... }
// orderService.ts
import { UserService } from './userService';
export class OrderService { ... }
After:
// types/interfaces.ts (new file - no imports from services)
export interface IUserService { ... }
export interface IOrderService { ... }
// userService.ts
import { IOrderService } from '../types/interfaces';
export class UserService implements IUserService { ... }
Strategy 2: Dependency Injection
// orderService.ts
export class OrderService {
constructor(private userService: IUserService) {}
// Instead of importing UserService directly
}
// main.ts
const userService = new UserService();
const orderService = new OrderService(userService);
Strategy 3: Dynamic Imports
// Only import when needed, not at module level
async function processOrder() {
const { UserService } = await import('./userService');
// ...
}
Strategy 4: Use Type-Only Imports
If you only need types (not values), use type-only imports:
// This doesn't create a runtime dependency
import type { User } from './userService';
Strategy 5: Restructure Barrel Files
Before (problematic):
// components/index.ts
export * from './Button';
export * from './Modal'; // Modal imports Button from './index'
After:
// components/Modal.tsx
import { Button } from './Button'; // Direct import, not from index
Step 4: Prevent Future Cycles
Add to your CI/build process:
// package.json
{
"scripts": {
"check:circular": "madge --circular --extensions ts,tsx src/"
}
}
Or configure ESLint:
// .eslintrc.js
module.exports = {
plugins: ['import'],
rules: {
'import/no-cycle': ['error', { maxDepth: 10 }]
}
}
Verification
- Run
madge --circular src/- should report no cycles - Run your test suite - previously undefined imports should work
- Delete
node_modulesand reinstall - app should still work - Build for production - no runtime errors
Example
Problem: OrderService is undefined when imported in UserService
Detection:
$ madge --circular src/
Circular dependencies found!
src/services/userService.ts → src/services/orderService.ts → src/services/userService.ts
Fix: Extract shared interface
// NEW: src/types/services.ts
export interface IOrderService {
createOrder(userId: string): Promise<Order>;
}
// MODIFIED: src/services/userService.ts
import type { IOrderService } from '../types/services';
export class UserService {
constructor(private orderService: IOrderService) {}
}
// MODIFIED: src/services/orderService.ts
// No longer imports UserService
export class OrderService implements IOrderService {
async createOrder(userId: string): Promise<Order> { ... }
}
Notes
- TypeScript
import typeis your friend—it's erased at runtime and can't cause cycles - Barrel files (
index.ts) are a common source of accidental cycles - The order of exports in a file can matter when there's a cycle
- Jest/Vitest may handle module resolution differently than your bundler
- Some bundlers (Webpack, Vite) have better cycle handling than others
require()can sometimes mask circular dependency issues thatimportexposes
When not to use it
- →When the issue is not related to module initialization order
- →When the project does not use TypeScript or JavaScript modules
Prerequisites
Limitations
- →Some bundlers may handle cycles differently than others
- →Requires manual refactoring of code structures
- →Barrel files often require manual restructuring
How it compares
This approach provides a systematic method to identify and resolve runtime initialization errors rather than relying on trial-and-error import reordering.
Compared to similar skills
typescript-circular-dependency side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| typescript-circular-dependency (this skill) | 1 | 6mo | Review | Advanced |
| javascript-mastery | 7 | 6mo | No flags | Beginner |
| tech-debt-analyzer | 5 | 9mo | Review | Intermediate |
| error-handling | 2 | 2mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by blader
View all by blader →You might also like
javascript-mastery
davila7
Comprehensive JavaScript reference covering 33+ essential concepts every developer should know. From fundamentals like primitives and closures to advanced patterns like async/await and functional programming. Use when explaining JS concepts, debugging JavaScript issues, or teaching JavaScript fundamentals.
tech-debt-analyzer
ailabs-393
This skill should be used when analyzing technical debt in a codebase, documenting code quality issues, creating technical debt registers, or assessing code maintainability. Use this for identifying code smells, architectural issues, dependency problems, missing documentation, security vulnerabilities, and creating comprehensive technical debt documentation.
error-handling
EpicenterHQ
Error handling patterns using wellcrafted trySync and tryAsync. Use when writing error handling code, using try-catch blocks, or working with Result types and graceful error recovery.
codex-code-review
tyrchen
Perform comprehensive code reviews using OpenAI Codex CLI. This skill should be used when users request code reviews, want to analyze diffs/PRs, need security audits, performance analysis, or want automated code quality feedback. Supports reviewing staged changes, specific files, entire directories, or git diffs.
dead-code
parcadei
Find unused functions and dead code in the codebase
tech-debt
vm0-ai
Technical debt management - scan codebase for bad smells and create tracking issues