Skip to main content

Overview

The TypeScript Language Service provides rich IDE features like autocompletion, navigation, refactoring, and diagnostics. It’s built on top of the compiler but designed for interactive, incremental operation.
The language service powers editors like VS Code, Visual Studio, Sublime Text, and many others through the Language Server Protocol.

Architecture

Location: src/services/ (168+ TypeScript files) The language service layer sits between editors and the TypeScript compiler:

Core Components

Language Service

Main service interface providing IDE features

Document Registry

Caches parsed files and manages versions

TSServer

Server process handling editor requests

Protocol

JSON-based communication protocol

Language Service Interface

Location: src/services/services.ts The main service interface exposes IDE features:
interface LanguageService {
  // Completions
  getCompletionsAtPosition(
    fileName: string,
    position: number,
    options?: GetCompletionsAtPositionOptions
  ): CompletionInfo | undefined;
  
  getCompletionEntryDetails(
    fileName: string,
    position: number,
    name: string
  ): CompletionEntryDetails | undefined;
  
  // Navigation
  getDefinitionAtPosition(
    fileName: string,
    position: number
  ): DefinitionInfo[] | undefined;
  
  getDefinitionAndBoundSpan(
    fileName: string,
    position: number
  ): DefinitionInfoAndBoundSpan | undefined;
  
  findReferences(
    fileName: string,
    position: number
  ): ReferencedSymbol[] | undefined;
}

Key Features Implementation

Completions

Location: src/services/completions.ts The completion engine provides intelligent suggestions:
1

Context Analysis

Determine completion location (member access, import, type position)
2

Symbol Collection

Gather available symbols from scope, type checker, and imports
3

Filtering

Filter symbols by prefix and context appropriateness
4

Ranking

Sort by relevance using various heuristics
5

Details Generation

Provide type info, documentation, and auto-import suggestions
const obj = { foo: 1, bar: "hello" };
obj.| // Completions: foo, bar

// Implementation:
// 1. Get type of 'obj'
// 2. Get properties from type
// 3. Filter by accessibility
// 4. Return completion entries
Completions are position-based and incremental - they don’t require full type checking of the entire program.

Go to Definition

Location: src/services/goToDefinition.ts Navigates from usage to declaration:
Definition Resolution
// User clicks on 'foo' in:
const x = foo;

// Implementation flow:
// 1. Get symbol at position
const symbol = checker.getSymbolAtLocation(node);

// 2. Get symbol's declarations
const declarations = symbol?.declarations;

// 3. Return declaration locations
return declarations?.map(decl => ({
  fileName: decl.getSourceFile().fileName,
  textSpan: createTextSpanFromNode(decl),
}));
For local variables, functions, and classes - finds the declaration in the same file
Follows imports to the original declaration in another file or module
For library types, navigates to .d.ts declaration files
Handles function overloads, merged declarations, and augmentations

Find All References

Location: src/services/findAllReferences.ts Finds all usages of a symbol across the project:
1

Symbol Identification

Get the symbol at the current position
2

Related Symbols

Find related symbols (implementations, overrides, aliases)
3

File Search

Search source files for references
4

Context Classification

Classify references (read, write, declaration)
5

Results Grouping

Group results by file and context
// Different reference contexts:

function foo() {}  // Declaration
foo();             // Read reference

let bar = 1;       // Declaration  
bar = 2;           // Write reference
bar++;             // Read+Write reference

export { foo };    // Export reference

Rename

Location: src/services/rename.ts Renames symbols across all files while preserving semantics:
Rename is complex - it must handle scope conflicts, imports/exports, and string literal references.
Rename Process
// Renaming 'foo' to 'bar':

// 1. Check if rename is valid
if (newName is invalid or conflicts with scope) {
  return error;
}

// 2. Find all references
const references = findAllReferences(symbol);

// 3. Generate text changes for each file
const edits = references.map(ref => ({
  fileName: ref.fileName,
  textChanges: [{
    span: ref.textSpan,
    newText: newName
  }]
}));

// 4. Update imports/exports
if (symbol is exported) {
  updateImportStatements(edits);
}

Code Fixes

Location: src/services/codefixes/ Provides quick fixes for diagnostics:

Import Fixes

Add missing imports automatically

Type Fixes

Add missing type annotations

Declaration Fixes

Declare missing variables or properties

Spelling Fixes

Suggest corrections for typos
Each fix is implemented as a separate module:
src/services/codefixes/
├── fixAddMissingAwait.ts
├── fixAddMissingMember.ts  
├── fixClassIncorrectlyImplementsInterface.ts
├── fixImport.ts
├── fixMissingTypeAnnotationOnExports.ts
├── fixSpelling.ts
└── ... (50+ code fix providers)

Refactorings

Location: src/services/refactors/ Provides structural code transformations:
// Extract to function
const x = a + b;
const y = x * 2;

// Becomes:
function newFunction(a: number, b: number) {
  const x = a + b;
  return x * 2;
}
const y = newFunction(a, b);
Refactoring modules:
src/services/refactors/
├── convertToEsModule.ts
├── extractFunction.ts
├── extractSymbol.ts
├── extractType.ts
├── generateGetAccessorAndSetAccessor.ts
├── inferFunctionReturnType.ts
├── moveToFile.ts
└── ... (20+ refactoring providers)

Document Registry

Location: src/services/documentRegistry.ts Manages source file caching and versioning:
interface DocumentRegistry {
  acquireDocument(
    fileName: string,
    compilationSettings: CompilerOptions,
    scriptSnapshot: IScriptSnapshot,
    version: string
  ): SourceFile;
  
  updateDocument(
    fileName: string,
    compilationSettings: CompilerOptions,
    scriptSnapshot: IScriptSnapshot,
    version: string
  ): SourceFile;
  
  releaseDocument(
    fileName: string,
    compilationSettings: CompilerOptions
  ): void;
}
The registry enables incremental compilation by caching and reusing source files across program instances.

TSServer Protocol

Location: src/server/protocol.ts Defines the JSON-based communication protocol:

Request/Response Model

{
  "seq": 1,
  "type": "request",
  "command": "completions",
  "arguments": {
    "file": "/path/to/file.ts",
    "line": 10,
    "offset": 15
  }
}

Command Types

TSServer supports 100+ commands:
  • open - Open file for editing
  • close - Close file
  • change - Update file content
  • reload - Reload file from disk

Performance Optimizations

The language service reuses Program instances across edits when possible
Only modified files are reparsed; unchanged subtrees are reused
Type checking happens on-demand for visible files only
Long operations can be cancelled when new edits arrive
Diagnostics are computed in background (async geterr command)

Cancellation Tokens

Cancellable Operations
interface CancellationToken {
  isCancellationRequested(): boolean;
  throwIfCancellationRequested(): void;
}

// Used throughout services:
function getCompletions(
  fileName: string,
  position: number,
  cancellationToken?: CancellationToken
): CompletionInfo {
  // Check cancellation periodically
  cancellationToken?.throwIfCancellationRequested();
  
  // Expensive operation
  const symbols = getSymbols();
  
  cancellationToken?.throwIfCancellationRequested();
  
  return processSymbols(symbols);
}

Additional Features

Formatting

Location: src/services/formatting/ Provides code formatting based on rules:

Format Document

Format entire file

Format Selection

Format selected range

Format On Type

Format as user types

Format On Paste

Format pasted content

Organize Imports

Location: src/services/organizeImports.ts Sorts and removes unused imports:
// Before:
import { z, a, m } from "module";
import { unused } from "other";

// After organize imports:
import { a, m, z } from "module";
// 'unused' import removed if not referenced

Call Hierarchy

Location: src/services/callHierarchy.ts Provides call graph navigation:
// Find all callers of a function
function foo() { ... }

// Incoming calls: who calls foo?
bar() -> foo();
baz() -> foo();

// Outgoing calls: what does foo call?
foo() -> helper();
foo() -> util();

Inlay Hints

Location: src/services/inlayHints.ts Provides inline hints in editor:
// Shows inferred types and parameter names:
const x = getValue();  // : string

function foo(a, b) { ... }
foo(1, 2);  // a: ‸1, b: ‸2

Editor Integration

Editors integrate via different approaches:
# Editor spawns TSServer process
node tsserver.js --stdio

# Communicates via JSON-RPC over stdio
Editor <-> TSServer <-> Language Service

Testing

Location: src/harness/ The language service has extensive tests:
// Example test structure
describe("completions", () => {
  it("provides member completions", () => {
    const code = `
      const obj = { foo: 1 };
      obj./**/
    `;
    
    const completions = getCompletionsAtMarker();
    
    assert.contains(completions, "foo");
  });
});

Debugging Tips

1

Enable Logging

Set TSS_LOG environment variable to log TSServer requests/responses
2

Inspect Protocol

Use --logVerbosity verbose to see detailed protocol messages
3

Profile Performance

Use --enableTracing to generate performance traces
4

Debug TSServer

Attach debugger to TSServer process for breakpoints
export TSS_LOG="-level verbose -file /tmp/tsserver.log"

# TSServer will write detailed logs

Contributing to Language Service

Language service changes must be fast and not block the editor UI.

Best Practices

Accept and check CancellationToken in long operations
Use position-based APIs and early exits
Cache expensive computations with proper invalidation
Verify features work correctly with incremental updates

Next Steps

Architecture Overview

Review the overall TypeScript architecture

Compiler Internals

Deep dive into compiler implementation

Reference Files

Key language service files in src/services/:
  • services.ts - Main LanguageService interface
  • completions.ts - Completion engine
  • goToDefinition.ts - Definition navigation
  • findAllReferences.ts - Reference finding
  • rename.ts - Symbol renaming
  • documentRegistry.ts - File caching
  • codefixes/ - Quick fix providers
  • refactors/ - Refactoring providers
  • formatting/ - Code formatting
The language service is designed to be responsive and incremental - study how it avoids blocking operations.

Build docs developers (and LLMs) love