References

Import Organization

Rules for grouping, ordering, and typing imports.

Core Patterns

  • Import Grouping Order
  • Patterns
  • Common Pitfalls
  • Related Topics

Import Grouping Order

Group imports in this order, with blank lines between groups:

// 1. External libraries (node_modules)
import React, { useState, useEffect } from "react";
import { Button, TextField } from "@mui/material";

// 2. Internal modules (project code)
import { UserService } from "./services/UserService";
import { formatDate } from "./utils/date";
import { OrderList } from "./components/OrderList";

// 3. Types (always last)
import type { User, UserRole } from "./types";
import type { Order } from "./domain/entities/Order";

Patterns

✅ REQUIRED: Named Imports Over Namespace

// ✅ CORRECT: Named imports — explicit, tree-shakeable
import { readFileSync, existsSync } from 'fs';
import { join, resolve } from 'path';
import { load, dump } from 'js-yaml';

// ❌ WRONG: Namespace import when only using a few exports
import * as fs from 'fs';       // Only using readFileSync
import * as path from 'path';   // Only using join

// ✅ EXCEPTION: Namespace import OK when using 6+ exports
import * as p from '@clack/prompts'; // uses intro, spinner, select, multiselect, confirm, cancel, note, log

Rule: Named imports for <6 exports, namespace OK for 6+ exports.

✅ REQUIRED: Separate Type Imports

// ✅ CORRECT: import type for type-only imports
import { UserService } from './services/UserService';
import type { User, UserRole } from './types';

// ✅ CORRECT: Inline type import when mixing values and types
import { Installer, type Model } from '../core/installer';

// ❌ WRONG: Importing types as values (emits unnecessary JS)
import { User, UserRole } from './types';

Why: import type is erased at compile time, reducing bundle size and preventing circular dependency issues.

✅ REQUIRED: Prefer Static Imports

// ✅ CORRECT: Static import at module top
import fs from 'fs';
import { load } from 'js-yaml';

// ❌ WRONG: Dynamic import when static works
async function doWork() {
  const fs = await import('fs');   // unnecessary
  const yaml = require('js-yaml'); // CJS require in TS
}

// ✅ EXCEPTION: Dynamic import OK for code splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'));

✅ REQUIRED: No Unused Imports

// ❌ WRONG: Import never used
import { something } from './lib'; // never referenced

// ✅ CORRECT: Every import is used
import { needed } from './lib';
const count = needed();
// ✅ PREFERRED: Alphabetical within each group
import { Button } from "@mui/material";
import React from "react";
import { useSelector } from "react-redux";

import { formatDate } from "./utils/date";
import { UserService } from "./services/UserService";

import type { Order } from "./types/Order";
import type { User } from "./types/User";

Common Pitfalls

Circular dependencies: If module A imports from B and B imports from A → extract shared types to a third module.

Barrel exports (index.ts): Useful for public API, but avoid deep nesting. Re-export only what consumers need.

// ✅ CORRECT: Selective barrel export
// components/index.ts
export { UserProfile } from './UserProfile';
export { OrderList } from './OrderList';

// ❌ WRONG: Export everything
export * from './UserProfile';
export * from './OrderList';
export * from './internal/helpers'; // Don't expose internals