This page documents the Organize Imports feature of the TypeScript language service. It covers how the feature groups, sorts, coalesces, and removes unused import (and export) declarations in a source file. For the broader language service architecture, see Language Service. For code fixes that register with error codes to add/remove single imports, see Code Fixes and Diagnostics.
Organize Imports is implemented in src/services/organizeImports.ts and exposed through the LanguageService interface as organizeImports(). When invoked, it can perform up to three operations on a file's import and export declarations:
| Operation | Description |
|---|---|
| Remove Unused | Drops any import bindings not referenced in the file |
| Coalesce | Merges multiple import statements from the same module specifier |
| Sort | Reorders import groups by module specifier and named specifiers alphabetically |
Which operations run is governed by OrganizeImportsMode.
The OrganizeImportsMode enum (imported in src/services/organizeImports.ts52) selects the behavior:
| Mode | Remove Unused | Coalesce | Sort |
|---|---|---|---|
All | ✓ | ✓ | ✓ |
SortAndCombine | — | ✓ | ✓ |
RemoveUnused | ✓ | — | — |
The flags are resolved at src/services/organizeImports.ts85-89:
shouldSort = mode === SortAndCombine || mode === All
shouldCombine = shouldSort
shouldRemove = mode === RemoveUnused || mode === All
The top-level function is organizeImports at src/services/organizeImports.ts76-212
Organize Imports – Top-Level Data Flow
Sources: src/services/organizeImports.ts76-212
Inner Worker Pipeline
Sources: src/services/organizeImports.ts192-206 src/services/organizeImports.ts142-190
Before organizing, imports are split into groups separated by blank lines. Two imports belong to different groups if there are two or more newline characters between them (a single blank line). Comments (including multi-line comments) between two imports do not create a new group.
This is implemented by groupByNewlineContiguous src/services/organizeImports.ts224-241 which uses the TypeScript Scanner to count NewLineTrivia tokens in the leading trivia. The helper isNewGroup src/services/organizeImports.ts245-264 returns true when at least two newlines are found.
Each import group is organized independently, so blank-line-separated blocks of imports remain separated after organization.
Sources: src/services/organizeImports.ts224-264
When organizeImportsIgnoreCase is not explicitly set to a boolean value (i.e., it is "auto" or absent), the feature infers the preferred sort style from the existing import order in the file. This prevents the organizer from fighting a pre-existing case-sensitive sort with a case-insensitive one.
Two detection passes run:
| Function | What it detects |
|---|---|
detectModuleSpecifierCaseBySort | Whether module specifiers are sorted case-sensitively or insensitively |
detectNamedImportOrganizationBySort | Whether named specifiers are sorted case-sensitively/insensitively, and what organizeImportsTypeOrder is in use |
Both delegates use detectCaseSensitivityBySort src/services/organizeImports.ts784-808 which measures sortedness (number of out-of-order adjacent pairs) for each candidate comparer and picks the one that best matches the existing order.
measureSortedness src/services/organizeImports.ts774-782 counts adjacent pairs where comparer(arr[j], arr[j+1]) > 0.
Detection Flow
Sources: src/services/organizeImports.ts95-117 src/services/organizeImports.ts214-222 src/services/organizeImports.ts703-808
removeUnusedImports src/services/organizeImports.ts297-370 filters a group of ImportDeclaration nodes down to only those (or parts of those) that are referenced in the file.
Rules applied:
| Import form | Removal behavior |
|---|---|
Side-effect only (import "mod") | Always preserved |
Default import (import x from "mod") | Removed if x has no references |
Namespace import (import * as ns from "mod") | Removed if ns has no references |
Named import (import { a, b } from "mod") | Individual specifiers removed if unused; whole declaration removed if all are unused |
JSX factory name (React, Fragment, etc.) | Preserved when JSX elements are present and jsx mode requires explicit import |
| Module augmentation import | Preserved; in .d.ts files, the import clause is stripped but the bare import "mod" remains |
The reference check uses FindAllReferences.Core.isSymbolReferencedInFile src/services/organizeImports.ts368
Sources: src/services/organizeImports.ts297-370
coalesceImportsWorker src/services/organizeImports.ts422-536 merges multiple ImportDeclaration nodes that share the same module specifier.
Imports with with { ... } attributes are kept separate per attribute set. The attribute key is a normalized string of attribute name-value pairs src/services/organizeImports.ts427-437
getCategorizedImports src/services/organizeImports.ts385-420 splits each attribute group into:
importWithoutClause → side-effect-only: import "mod"
typeOnlyImports → { defaultImports, namespaceImports, namedImports }
regularImports → { defaultImports, namespaceImports, namedImports }
Coalesce rules per category (applied separately to regularImports and typeOnlyImports):
| Situation | Result |
|---|---|
| Single side-effect import | Kept as-is (duplicates dropped) |
| Multiple namespace imports | Each kept as a separate declaration |
| Exactly 1 default + 1 namespace, no named | Merged into import default, * as ns from "mod" |
| Multiple default imports | Rewritten as named imports (default as x) |
| Any named imports | All specifiers merged into one { ... } list |
| Type-only default + type-only named | Cannot be combined; emitted as two separate import type statements |
Named specifiers are sorted using getNamedImportSpecifierComparer which applies organizeImportsTypeOrder src/services/organizeImports.ts648-657:
organizeImportsTypeOrder | Named specifier sort |
|---|---|
"last" (default) | Non-type-only before type-only, then alphabetical |
"first" | Type-only before non-type-only, then alphabetical |
"inline" | Purely alphabetical, mixing type and non-type |
Sources: src/services/organizeImports.ts385-536
compareModuleSpecifiersWorker src/services/organizeImports.ts659-665 establishes the module specifier sort:
undefined specifiers (bare import "..." with dynamic expressions) last"react", "lodash") before relative ("./utils", "../helpers")Within a sorted group, compareImportKind src/services/organizeImports.ts810-834 orders different import forms:
| Order | Import form |
|---|---|
| 1 | Side-effect-only: import "mod" |
| 2 | Type-only: import type ... from "mod" |
| 3 | Namespace: import * as ns from "mod" |
| 4 | Default: import x from "mod" |
| 5 | Named: import { x } from "mod" |
| 6 | import X = require("mod") |
| 7 | const x = require("mod") |
Two string comparer variants are available, controlled by organizeImportsCollation:
| Setting | Comparer |
|---|---|
ordinal (default) | compareStringsCaseSensitive / compareStringsCaseInsensitiveEslintCompatible |
unicode | Intl.Collator with organizeImportsCaseFirst, organizeImportsNumericCollation, organizeImportsAccentCollation options |
Sources: src/services/organizeImports.ts659-665 src/services/organizeImports.ts810-870
Export declarations are also organized, but with different behavior:
organizeExportsWorker src/services/organizeImports.ts208-211 is called only when the mode is not RemoveUnused.coalesceExportsWorker src/services/organizeImports.ts562-632 merges export specifiers from the same module.export type { ... }) are kept separate from regular exports.getTopLevelExportGroups src/services/organizeImports.ts266-295 handles the different grouping logic for exports (non-specifier bare re-exports export * from "..." are grouped separately).Sources: src/services/organizeImports.ts208-211 src/services/organizeImports.ts266-295 src/services/organizeImports.ts562-632
The organizer also processes imports and exports inside declare module "..." blocks src/services/organizeImports.ts127-138 The same grouping, removal, coalescing, and sorting logic applies to the body statements of each ambient module, independently of the top-level imports.
Sources: src/services/organizeImports.ts127-138
All edits are accumulated through textChanges.ChangeTracker src/services/organizeImports.ts84 The organizeDeclsWorker function src/services/organizeImports.ts142-190 uses:
ChangeTracker.replaceNodeWithNodes — replaces the first import declaration with the full new listChangeTracker.deleteNodes — removes remaining original declarationsLeadingTriviaOption.Exclude — prevents the file's header comment from being moved with the first importTrailingTriviaOption.Include — preserves trailing comments on import linesThe function returns FileTextChanges[] from changeTracker.getChanges().
Sources: src/services/organizeImports.ts142-190
Code Entity Map: Language Service → Organize Imports
Sources: src/services/organizeImports.ts76-84
The organizeImports function is the sole public export from organizeImports.ts for production use. Several internal functions are also exported with a test prefix (e.g., testCoalesceImports, testCoalesceExports, compareModuleSpecifiers) for use in unit tests src/testRunner/unittests/services/organizeImports.ts55-56
UserPreferences field | Effect |
|---|---|
organizeImportsIgnoreCase | true = always case-insensitive; false = always case-sensitive; "auto" = detect from file |
organizeImportsTypeOrder | "last" / "first" / "inline" — where type specifiers appear in named import lists |
organizeImportsCollation | "ordinal" or "unicode" — which string comparison algorithm to use |
organizeImportsCaseFirst | (unicode only) "upper" / "lower" / false |
organizeImportsNumericCollation | (unicode only) treat "9" < "10" |
organizeImportsAccentCollation | (unicode only) control accent sensitivity |
Sources: src/services/organizeImports.ts840-870
| Situation | Behavior |
|---|---|
| File has syntax errors | If skipDestructiveCodeActions is set, no changes are returned (no-op). If unset, organize still runs on valid portions. See test baselines in tests/baselines/reference/organizeImports/Syntax_Error_Body.ts and tests/baselines/reference/organizeImports/Syntax_Error_Imports_skipDestructiveCodeActions.ts |
Shorthand ambient module (declare module '*') | Does not crash; body is undefined so the loop skips it |
| Module augmentation imports | Not removed even if unused; in .d.ts files, rewritten to bare import "mod" |
JSX factory import (React) | Retained when JSX elements are present and jsx mode requires explicit factory imports |
Template literal module specifiers (`${'lib'}`) | Cannot be statically sorted; treated as non-sortable and left in place |
| Identical result | If the organized output equals the original text, getChanges() returns an empty array |
Sources: src/services/organizeImports.ts297-370 src/testRunner/unittests/services/organizeImports.ts370-388
Unit tests live in src/testRunner/unittests/services/organizeImports.ts They cover:
OrganizeImports.compareModuleSpecifiers directly to assert orderingOrganizeImports.testCoalesceImports and testCoalesceExports with parsed import arrayslanguageService.organizeImports() on a virtual file system and compare output to files in tests/baselines/reference/organizeImports/detection1 through detection10)Fourslash tests in tests/cases/fourslash/ (e.g., organizeImports4.ts through organizeImports17.ts, organizeImportsGroup_*.ts) test comment-trivia handling, group boundary detection, and mode-specific behavior.
Sources: src/testRunner/unittests/services/organizeImports.ts1-100 tests/cases/fourslash/organizeImports17.ts tests/cases/fourslash/organizeImportsGroup_Newline.ts
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.