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:
Core Features
Refactoring & Edits
Analysis
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:
Context Analysis
Determine completion location (member access, import, type position)
Symbol Collection
Gather available symbols from scope, type checker, and imports
Filtering
Filter symbols by prefix and context appropriateness
Ranking
Sort by relevance using various heuristics
Details Generation
Provide type info, documentation, and auto-import suggestions
Member Completions
Import Completions
Auto-Import
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
import { | } from "module" ;
// ^ Completions: exported symbols
// Implementation:
// 1. Resolve module specifier
// 2. Get module's exports
// 3. Return exportable symbols
path . | // Suggests 'join' with auto-import
// Generates:
import { join } from "path" ;
// Implementation:
// 1. Search workspace for symbols
// 2. Check if accessible via import
// 3. Generate import statement
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:
// 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:
Symbol Identification
Get the symbol at the current position
Related Symbols
Find related symbols (implementations, overrides, aliases)
File Search
Search source files for references
Context Classification
Classify references (read, write, declaration)
Results Grouping
Group results by file and context
Reference Types
Finding References
// 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.
// 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:
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:
Registry Interface
Incremental Updates
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
Request Format
Response Format
Event Format
{
"seq" : 1 ,
"type" : "request" ,
"command" : "completions" ,
"arguments" : {
"file" : "/path/to/file.ts" ,
"line" : 10 ,
"offset" : 15
}
}
Command Types
TSServer supports 100+ commands:
File Operations
Navigation
Editing
Diagnostics
open - Open file for editing
close - Close file
change - Update file content
reload - Reload file from disk
definition - Go to definition
implementation - Find implementations
references - Find all references
navtree - Get navigation tree
navbar - Get navigation bar
completions - Get completions
quickinfo - Get hover information
signatureHelp - Get signature help
codefix - Get quick fixes
refactor - Get refactorings
geterr - Get diagnostics (async)
semanticDiagnosticsSync - Get semantic diagnostics
syntacticDiagnosticsSync - Get syntactic diagnostics
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
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
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:
TSServer (Most Common)
Direct API
Language Server Protocol
# Editor spawns TSServer process
node tsserver.js --stdio
# Communicates via JSON-RPC over stdio
Editor < - > TSServer < - > Language Service
// In-process usage (e.g., webpack)
import * as ts from "typescript" ;
const service = ts . createLanguageService (
host ,
registry
);
const completions = service . getCompletionsAtPosition ( ... );
# Some editors use LSP wrapper
Editor < - > LSP Client < - > TSServer Adapter < - > TSServer
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
Enable Logging
Set TSS_LOG environment variable to log TSServer requests/responses
Inspect Protocol
Use --logVerbosity verbose to see detailed protocol messages
Profile Performance
Use --enableTracing to generate performance traces
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
Avoid Full Program Traversal
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.