Featured image of post TypeScript Path Aliases: Clean Imports at Scale Featured image of post TypeScript Path Aliases: Clean Imports at Scale

TypeScript Path Aliases: Clean Imports at Scale

Guide to TypeScript path aliases covering tsconfig paths, baseUrl, wildcard patterns, bundler integration, ESLint resolution, and monorepo strategies.

TypeScript path aliases eliminate the pain of deep relative imports like ../../../utils/helpers, replacing them with clean, intention-revealing paths such as @utils/helpers. This article covers configuration, bundler integration, testing, and migration strategies.

Configuring baseUrl and paths

The foundation of path aliases is the paths mapping in tsconfig.json, combined with baseUrl:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@utils/*": ["./src/utils/*"]
    }
  }
}

baseUrl sets the base directory for resolving non-relative module names. paths maps alias prefixes to file system locations relative to baseUrl. The wildcard * matches any path segment after the prefix.


Wildcard Patterns and Advanced Mappings

Wildcards support flexible mapping strategies:

{
  "paths": {
    "@shared/*": ["./src/shared/*"],
    "@components/*": ["./src/components/*", "./src/shared/components/*"],
    "@config": ["./src/config/index.ts"]
  }
}

Multiple fallback paths are searched in order if the first location does not exist. Direct module aliases (without wildcards) are useful for specific entry points.


Bundler Integration

TypeScript’s paths only affects type-checking and IDE support. Every bundler needs its own alias configuration:

ToolConfiguration
webpackresolve.alias: { '@': path.resolve(__dirname, 'src') }
Viteresolve.alias: { '@': '/src' } (or vite-tsconfig-paths plugin)
esbuild--alias:@=./src
SWCjsc.paths mirroring tsconfig

For Vite, the vite-tsconfig-paths plugin reads your tsconfig automatically, eliminating duplication:

import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  plugins: [tsconfigPaths()],
});

For Node.js runtime resolution, use the tsconfig-paths package with tsconfig-paths/register.


ESLint Resolution

ESLint’s import/no-unresolved rule will fail on aliased imports without proper resolver configuration. The eslint-import-resolver-typescript package reads tsconfig paths automatically:

settings: {
  'import/resolver': {
    typescript: { alwaysTryTypes: true }
  }
}

You can also configure import/order to group aliased imports separately:

'import/order': ['warn', {
  groups: ['builtin', 'external', 'internal', 'parent', 'sibling'],
  pathGroups: [
    { pattern: '@/**', group: 'internal', position: 'before' }
  ]
}]

Monorepo Strategies

In monorepos, each package typically has its own tsconfig.json with path aliases:

  • Nx: Project-level tsconfig with root tsconfig for shared paths
  • Turborepo: Each package defines its own paths; workspace protocol for cross-package imports
  • pnpm workspaces: Prefer workspace protocol over path aliases for cross-package references

TypeScript project references (composite + references) offer an alternative to path aliases in monorepos, providing incremental compilation and faster builds.


Testing with Path Aliases

Test runners also need alias configuration:

// Jest - moduleNameMapper
{ "^@/(.*)$": "<rootDir>/src/$1" }

// Vitest - automatically uses Vite's resolve.alias
import { defineConfig } from 'vitest/config';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  plugins: [tsconfigPaths()],
});

Migration Strategy

Adopt path aliases incrementally. Start with a single @/ alias for new code, then refactor existing imports using codemods like ts-migrate or custom jscodeshift transforms. After migration, enforce conventions with import/no-relative-parent-imports in ESLint.

Avoid common pitfalls: circular dependencies become harder to spot with aliases, and over-alias (creating aliases for every directory) adds cognitive load without benefit.


Path aliases significantly improve code maintainability in mid-to-large TypeScript projects. The key challenge is keeping bundler, test runner, and linter configurations synchronized. Using plugins that auto-read tsconfig reduces this burden substantially.