Fundamentals 14 min read

Understanding TypeScript: Compilation Pipeline, VSCode Integration, LSP, Babel, and tsc

This article explains TypeScript’s compilation pipeline, AST and symbol system, its integration with VSCode via the Language Server Protocol, how Babel can strip types for bundling, and how to enforce type safety with the tsc command, providing code examples throughout.

ByteFE
ByteFE
ByteFE
Understanding TypeScript: Compilation Pipeline, VSCode Integration, LSP, Babel, and tsc

TypeScript is a superset of JavaScript that adds a static type‑checking system, improving code readability and maintainability.

The compilation process consists of scanning source code into tokens, parsing tokens into an AST, binding symbols to AST nodes, checking types, and finally emitting JavaScript; the AST is the core of type verification.

Each AST node (Node) holds type information and location data, while a Symbol represents the external identity of a node, containing members and exports. Nodes have a locals property for declared identifiers, and Symbols have a declarations array linking back to their declaration nodes, as illustrated by interface merging examples.

The binder traverses the AST to create Symbols and associate them with related nodes, enabling the checker to validate assignments, imports, and method calls; diagnostics are stored in the SourceFile node’s diagnostics property.

VSCode includes a built‑in TypeScript plugin that follows the Language Server Protocol (LSP). The LSP architecture separates the client (running inside VSCode, written in JS/TS) from the language server (the tsserver process) which performs lexical, syntactic, and semantic analysis.

private semanticCheck(file: NormalizedPath, project: Project) {
    // simplified
    const diags = project.getLanguageService().getSemanticDiagnostics(file).filter(d => !!d.file);
    this.sendDiagnosticsEvent(file, project, diags, "semanticDiag");
}

The client creates the server by forking the tsserver.js file:

this._factory.fork(version.tsServerPath, args, kind, configuration, this._versionManager)

Diagnostics are managed by a DiagnosticsManager class that creates a VSCode diagnostic collection and updates it asynchronously:

class DiagnosticsManager {
    constructor(owner: string, onCaseInsenitiveFileSystem: boolean) {
        super();
        // create caches for performance
        this._diagnostics = new ResourceMap
(undefined, { onCaseInsenitiveFileSystem });
        this._pendingUpdates = new ResourceMap
(undefined, { onCaseInsenitiveFileSystem });
        this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner));
    }
    public updateDiagnostics(file: vscode.Uri, language: DiagnosticLanguage, kind: DiagnosticKind, diagnostics: ReadonlyArray
): void {
        const fileDiagnostics = new FileDiagnostics(file, language);
        fileDiagnostics.updateDiagnostics(language, kind, diagnostics);
        this._diagnostics.set(file, fileDiagnostics);
        this.scheduleDiagnosticsUpdate(file);
    }
    private scheduleDiagnosticsUpdate(file: vscode.Uri) {
        if (!this._pendingUpdates.has(file)) {
            this._pendingUpdates.set(file, setTimeout(() => this.updateCurrentDiagnostics(file), this._updateDelay));
        }
    }
    private updateCurrentDiagnostics(file: vscode.Uri): void {
        if (this._pendingUpdates.has(file)) {
            clearTimeout(this._pendingUpdates.get(file));
            this._pendingUpdates.delete(file);
        }
        const fileDiagnostics = this._diagnostics.get(file);
        this._currentDiagnostics.set(file, fileDiagnostics ? fileDiagnostics.getDiagnostics(this._settings) : []);
    }
}

When Babel is used to transpile TypeScript, the @babel/preset-typescript plugin removes type annotations. A typical Babel configuration and webpack rule look like:

{
    "presets": ["@babel/preset-typescript"]
}
{
    "rules" [
        {
            "test": /.ts$/,
            "use": "label-loader"
        }
    ]
}

Babel’s single‑file processing means it cannot perform full type checking; therefore developers still rely on editors for diagnostics and on the TypeScript compiler for strict verification.

To enforce type safety in CI or pre‑commit hooks, the tsc command can be run without emitting JavaScript:

tsc --noEmit --skipLibCheck

The article concludes that understanding TypeScript’s internals and the surrounding tooling (VSCode, LSP, Babel, tsc) helps developers leverage its benefits effectively.

typescriptASTBabelVSCodeLanguage Server Protocoltsc
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.