ESLint Plugin Import X provides specialized rules and configurations for seamless TypeScript integration, including type-only import handling, consistent type specifier styles, and TypeScript-aware module resolution.
Enforces consistent usage of type specifiers in import/export statements.
const consistentTypeSpecifierStyle: RuleModule<'typeOverValue' | 'valueOverType', [
'prefer-inline' | 'prefer-top-level'
]>;Options:
'prefer-inline': Prefer inline type specifiers (import { type Foo })'prefer-top-level': Prefer top-level type imports (import type { Foo })Example with prefer-inline:
// ✗ BAD - should use inline type specifiers
import type { User, UserConfig } from './types';
import { createUser, validateUser } from './types';
// ✓ GOOD - inline type specifiers
import { type User, type UserConfig, createUser, validateUser } from './types';Example with prefer-top-level:
// ✗ BAD - should separate type and value imports
import { type User, createUser } from './types';
// ✓ GOOD - separate type and value imports
import type { User } from './types';
import { createUser } from './types';Forbids mixing import declarations with module.exports (prevents TypeScript/CommonJS conflicts).
const noImportModuleExports: RuleModule<'notBothAllowed', [
{
exceptions?: string[];
}?
]>;Options:
exceptions (string[]): File patterns to exclude from this ruleExample:
// ✗ BAD - mixing ES imports with CommonJS exports
import React from 'react';
import { Component } from 'react';
module.exports = MyComponent;
// ✓ GOOD - consistent ES module syntax
import React, { Component } from 'react';
export default MyComponent;
// ✓ GOOD - consistent CommonJS syntax
const React = require('react');
const { Component } = require('react');
module.exports = MyComponent;The TypeScript configuration preset provides optimal settings for TypeScript projects.
const typescript: PluginConfig = {
settings: {
'import-x/extensions': ['.ts', '.tsx', '.js', '.jsx'],
'import-x/external-module-folders': ['node_modules', 'node_modules/@types'],
'import-x/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx', '.cts', '.mts']
},
'import-x/resolver': {
node: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
}
}
},
rules: {
'import-x/named': 'off' // TypeScript handles this at compile time
}
};Key Features:
.ts, .tsx, .cts, .mts)@types packages in module resolution@typescript-eslint/parser for TypeScript filesnamed rule (TypeScript compiler handles this)// Type-only imports (don't affect runtime)
import type { User, Config } from './types';
import type { ComponentProps } from 'react';
// Mixed imports with inline type specifiers
import { type User, createUser, type Config } from './api';
// Re-exporting types
export type { User } from './types';
export type { ComponentProps } from 'react';
// Default type imports
import type React from 'react';
import type { default as Logger } from './logger';The plugin automatically detects type-only imports and handles them appropriately:
// These imports affect the import order and dependency analysis differently
import type { TypeDef } from './types'; // Type-only
import { RuntimeValue } from './values'; // Runtime value
import { type TypeDef2, runtimeFn } from './mixed'; // Mixed{
"import-x/resolver": {
"typescript": {
"alwaysTryTypes": true,
"project": "./tsconfig.json"
},
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx", ".d.ts"]
}
}
}// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@types/*": ["types/*"],
"@utils/*": ["src/utils/*"]
}
}
}
// ESLint config
{
"import-x/resolver": {
"typescript": {
"project": "./tsconfig.json"
}
}
}// For TypeScript project references
{
"import-x/resolver": {
"typescript": {
"project": ["./tsconfig.json", "packages/*/tsconfig.json"]
}
}
}// ESLint config for TypeScript monorepo
{
"import-x/resolver": {
"typescript": {
"project": [
"./tsconfig.json",
"./packages/*/tsconfig.json",
"./apps/*/tsconfig.json"
],
"alwaysTryTypes": true
}
},
"import-x/external-module-folders": [
"node_modules",
"node_modules/@types",
"packages/*/node_modules/@types"
]
}// types.d.ts - declaration files are handled specially
declare module 'legacy-module' {
export function legacyFn(): void;
}
declare global {
interface Window {
customProperty: string;
}
}
// Usage - imports from declaration files
import { legacyFn } from 'legacy-module'; // Resolved via .d.tsThe plugin respects TypeScript's type resolution order:
.d.ts files@types/package-name packagespackage.json types/typings fieldindex.d.ts in package root// The plugin understands complex TypeScript patterns
export type EventMap<T> = {
[K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void;
};
// Import/export of complex types
import type { EventMap } from './types';
export type { EventMap };// Module augmentation is properly recognized
declare module 'express' {
interface Request {
user?: User;
}
}
// Importing augmented modules
import express from 'express'; // Includes augmentationsinterface TypeScriptResolver {
alwaysTryTypes?: boolean;
project?: string | string[];
tsconfigRootDir?: string;
extensions?: string[];
moduleDirectory?: string[];
conditionNames?: string[];
}
interface TypeImportInfo {
isTypeOnly: boolean;
isInlineType: boolean;
isDefaultTypeImport: boolean;
specifiers: TypeSpecifier[];
}
interface TypeSpecifier {
name: string;
isType: boolean;
isDefault: boolean;
alias?: string;
}
type TypeSpecifierStyle = 'prefer-inline' | 'prefer-top-level';
interface TypeScriptParserOptions {
jsx?: boolean;
useJSXTextNode?: boolean;
project?: string | string[];
tsconfigRootDir?: string;
extraFileExtensions?: string[];
}// Component prop types
import type { FC, ReactNode } from 'react';
interface ButtonProps {
children: ReactNode;
onClick: () => void;
variant?: 'primary' | 'secondary';
}
const Button: FC<ButtonProps> = ({ children, onClick, variant = 'primary' }) => {
return <button onClick={onClick} className={variant}>{children}</button>;
};
export default Button;
export type { ButtonProps };// API types and runtime code separation
import type { User, CreateUserRequest, ApiResponse } from './types';
import { apiClient } from './client';
export async function createUser(data: CreateUserRequest): Promise<ApiResponse<User>> {
return apiClient.post('/users', data);
}
export type { User, CreateUserRequest, ApiResponse };// Type utility library
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
export type RequireOne<T, K extends keyof T = keyof T> =
K extends keyof T ? T & Required<Pick<T, K>> : never;
// Usage
import type { DeepPartial, RequireOne } from './type-utils';plugin:import-x/typescripttsconfig.json.d.ts files in resolver extensions