diff --git a/extensions/typescript-language-features/web/src/fileWatcherManager.ts b/extensions/typescript-language-features/web/src/fileWatcherManager.ts
index f1b6f90f292b7..8c8d7403740ec 100644
--- a/extensions/typescript-language-features/web/src/fileWatcherManager.ts
+++ b/extensions/typescript-language-features/web/src/fileWatcherManager.ts
@@ -3,11 +3,20 @@
  *  Licensed under the MIT License. See License.txt in the project root for license information.
  *--------------------------------------------------------------------------------------------*/
 
-import * as ts from 'typescript/lib/tsserverlibrary';
+import type * as ts from 'typescript/lib/tsserverlibrary';
 import { URI } from 'vscode-uri';
 import { Logger } from './logging';
 import { PathMapper, fromResource, looksLikeLibDtsPath, looksLikeNodeModules, mapUri } from './pathMapper';
 
+/**
+ * Copied from `ts.FileWatcherEventKind` to avoid direct dependency.
+ */
+enum FileWatcherEventKind {
+	Created = 0,
+	Changed = 1,
+	Deleted = 2,
+}
+
 export class FileWatcherManager {
 	private static readonly noopWatcher: ts.FileWatcher = { close() { } };
 
@@ -107,11 +116,11 @@ export class FileWatcherManager {
 
 	private toTsWatcherKind(event: 'create' | 'change' | 'delete') {
 		if (event === 'create') {
-			return ts.FileWatcherEventKind.Created;
+			return FileWatcherEventKind.Created;
 		} else if (event === 'change') {
-			return ts.FileWatcherEventKind.Changed;
+			return FileWatcherEventKind.Changed;
 		} else if (event === 'delete') {
-			return ts.FileWatcherEventKind.Deleted;
+			return FileWatcherEventKind.Deleted;
 		}
 		throw new Error(`Unknown event: ${event}`);
 	}
diff --git a/extensions/typescript-language-features/web/src/serverHost.ts b/extensions/typescript-language-features/web/src/serverHost.ts
index ef03ace9e59f2..f2f9ca95996b2 100644
--- a/extensions/typescript-language-features/web/src/serverHost.ts
+++ b/extensions/typescript-language-features/web/src/serverHost.ts
@@ -6,32 +6,16 @@
 import { ApiClient, FileStat, FileType, Requests } from '@vscode/sync-api-client';
 import { ClientConnection } from '@vscode/sync-api-common/browser';
 import { basename } from 'path';
-import * as ts from 'typescript/lib/tsserverlibrary';
+import type * as ts from 'typescript/lib/tsserverlibrary';
 import { FileWatcherManager } from './fileWatcherManager';
 import { Logger } from './logging';
 import { PathMapper, looksLikeNodeModules, mapUri } from './pathMapper';
 import { findArgument, hasArgument } from './util/args';
 
-// BEGIN misc internals
-const combinePaths: (path: string, ...paths: (string | undefined)[]) => string = (ts as any).combinePaths;
-const byteOrderMarkIndicator = '\uFEFF';
-const matchFiles: (
-	path: string,
-	extensions: readonly string[] | undefined,
-	excludes: readonly string[] | undefined,
-	includes: readonly string[] | undefined,
-	useCaseSensitiveFileNames: boolean,
-	currentDirectory: string,
-	depth: number | undefined,
-	getFileSystemEntries: (path: string) => { files: readonly string[]; directories: readonly string[] },
-	realpath: (path: string) => string
-) => string[] = (ts as any).matchFiles;
-const generateDjb2Hash = (ts as any).generateDjb2Hash;
-// End misc internals
-
 type ServerHostWithImport = ts.server.ServerHost & { importPlugin(root: string, moduleName: string): Promise<ts.server.ModuleImportResult> };
 
 function createServerHost(
+	ts: typeof import('typescript/lib/tsserverlibrary'),
 	logger: Logger,
 	apiClient: ApiClient | undefined,
 	args: readonly string[],
@@ -43,6 +27,22 @@ function createServerHost(
 	const currentDirectory = '/';
 	const fs = apiClient?.vscode.workspace.fileSystem;
 
+	// Internals
+	const combinePaths: (path: string, ...paths: (string | undefined)[]) => string = (ts as any).combinePaths;
+	const byteOrderMarkIndicator = '\uFEFF';
+	const matchFiles: (
+		path: string,
+		extensions: readonly string[] | undefined,
+		excludes: readonly string[] | undefined,
+		includes: readonly string[] | undefined,
+		useCaseSensitiveFileNames: boolean,
+		currentDirectory: string,
+		depth: number | undefined,
+		getFileSystemEntries: (path: string) => { files: readonly string[]; directories: readonly string[] },
+		realpath: (path: string) => string
+	) => string[] = (ts as any).matchFiles;
+	const generateDjb2Hash = (ts as any).generateDjb2Hash;
+
 	// Legacy web
 	const memoize: <T>(callback: () => T) => () => T = (ts as any).memoize;
 	const ensureTrailingDirectorySeparator: (path: string) => string = (ts as any).ensureTrailingDirectorySeparator;
@@ -404,6 +404,7 @@ function createServerHost(
 }
 
 export async function createSys(
+	ts: typeof import('typescript/lib/tsserverlibrary'),
 	args: readonly string[],
 	fsPort: MessagePort,
 	logger: Logger,
@@ -418,10 +419,10 @@ export async function createSys(
 
 		const apiClient = new ApiClient(connection);
 		const fs = apiClient.vscode.workspace.fileSystem;
-		const sys = createServerHost(logger, apiClient, args, watchManager, pathMapper, enabledExperimentalTypeAcquisition, onExit);
+		const sys = createServerHost(ts, logger, apiClient, args, watchManager, pathMapper, enabledExperimentalTypeAcquisition, onExit);
 		return { sys, fs };
 	} else {
-		return { sys: createServerHost(logger, undefined, args, watchManager, pathMapper, false, onExit) };
+		return { sys: createServerHost(ts, logger, undefined, args, watchManager, pathMapper, false, onExit) };
 	}
 }
 
diff --git a/extensions/typescript-language-features/web/src/typingsInstaller/typingsInstaller.ts b/extensions/typescript-language-features/web/src/typingsInstaller/typingsInstaller.ts
index 7b9b164c40c52..7c40993d6df29 100644
--- a/extensions/typescript-language-features/web/src/typingsInstaller/typingsInstaller.ts
+++ b/extensions/typescript-language-features/web/src/typingsInstaller/typingsInstaller.ts
@@ -34,7 +34,7 @@ type InstallerResponse = ts.server.PackageInstalledResponse | ts.server.SetTypin
  * The "server" part of the "server/client" model. This is the part that
  * actually gets instantiated and passed to tsserver.
  */
-export default class WebTypingsInstallerClient implements ts.server.ITypingsInstaller {
+export class WebTypingsInstallerClient implements ts.server.ITypingsInstaller {
 
 	private projectService: ts.server.ProjectService | undefined;
 
diff --git a/extensions/typescript-language-features/web/src/util/args.ts b/extensions/typescript-language-features/web/src/util/args.ts
index a0f1cf10179e6..8a9224ddf6b8f 100644
--- a/extensions/typescript-language-features/web/src/util/args.ts
+++ b/extensions/typescript-language-features/web/src/util/args.ts
@@ -2,7 +2,7 @@
  *  Copyright (c) Microsoft Corporation. All rights reserved.
  *  Licensed under the MIT License. See License.txt in the project root for license information.
  *--------------------------------------------------------------------------------------------*/
-import * as ts from 'typescript/lib/tsserverlibrary';
+import type * as ts from 'typescript/lib/tsserverlibrary';
 
 export function hasArgument(args: readonly string[], name: string): boolean {
 	return args.indexOf(name) >= 0;
@@ -20,14 +20,23 @@ export function findArgumentStringArray(args: readonly string[], name: string):
 	return arg === undefined ? [] : arg.split(',').filter(name => name !== '');
 }
 
+/**
+ * Copied from `ts.LanguageServiceMode` to avoid direct dependency.
+ */
+export enum LanguageServiceMode {
+	Semantic = 0,
+	PartialSemantic = 1,
+	Syntactic = 2,
+}
+
 export function parseServerMode(args: readonly string[]): ts.LanguageServiceMode | string | undefined {
 	const mode = findArgument(args, '--serverMode');
 	if (!mode) { return undefined; }
 
 	switch (mode.toLowerCase()) {
-		case 'semantic': return ts.LanguageServiceMode.Semantic;
-		case 'partialsemantic': return ts.LanguageServiceMode.PartialSemantic;
-		case 'syntactic': return ts.LanguageServiceMode.Syntactic;
+		case 'semantic': return LanguageServiceMode.Semantic;
+		case 'partialsemantic': return LanguageServiceMode.PartialSemantic;
+		case 'syntactic': return LanguageServiceMode.Syntactic;
 		default: return mode;
 	}
 }
diff --git a/extensions/typescript-language-features/web/src/webServer.ts b/extensions/typescript-language-features/web/src/webServer.ts
index 3747bf1cfa7e5..0ea25aee76525 100644
--- a/extensions/typescript-language-features/web/src/webServer.ts
+++ b/extensions/typescript-language-features/web/src/webServer.ts
@@ -12,7 +12,7 @@ import { Logger, parseLogLevel } from './logging';
 import { PathMapper } from './pathMapper';
 import { createSys } from './serverHost';
 import { findArgument, findArgumentStringArray, hasArgument, parseServerMode } from './util/args';
-import { StartSessionOptions, WorkerSession } from './workerSession';
+import { StartSessionOptions, createWorkerSession } from './workerSession';
 
 const setSys: (s: ts.System) => void = (ts as any).setSys;
 
@@ -42,11 +42,11 @@ async function initializeSession(
 	const pathMapper = new PathMapper(extensionUri);
 	const watchManager = new FileWatcherManager(ports.watcher, extensionUri, enabledExperimentalTypeAcquisition, pathMapper, logger);
 
-	const { sys, fs } = await createSys(args, ports.sync, logger, watchManager, pathMapper, () => {
+	const { sys, fs } = await createSys(ts, args, ports.sync, logger, watchManager, pathMapper, () => {
 		removeEventListener('message', listener);
 	});
 	setSys(sys);
-	session = new WorkerSession(sys, fs, sessionOptions, ports.tsserver, pathMapper, logger);
+	session = createWorkerSession(ts, sys, fs, sessionOptions, ports.tsserver, pathMapper, logger);
 	session.listen();
 }
 
diff --git a/extensions/typescript-language-features/web/src/workerSession.ts b/extensions/typescript-language-features/web/src/workerSession.ts
index ec1007570337b..c752654b7d601 100644
--- a/extensions/typescript-language-features/web/src/workerSession.ts
+++ b/extensions/typescript-language-features/web/src/workerSession.ts
@@ -3,16 +3,13 @@
  *  Licensed under the MIT License. See License.txt in the project root for license information.
  *--------------------------------------------------------------------------------------------*/
 import { FileSystem } from '@vscode/sync-api-client';
-import * as ts from 'typescript/lib/tsserverlibrary';
+import type * as ts from 'typescript/lib/tsserverlibrary';
 import { Logger } from './logging';
-import WebTypingsInstaller from './typingsInstaller/typingsInstaller';
+import { WebTypingsInstallerClient } from './typingsInstaller/typingsInstaller';
 import { hrtime } from './util/hrtime';
 import { WasmCancellationToken } from './wasmCancellationToken';
 import { PathMapper } from './pathMapper';
 
-const indent: (str: string) => string = (ts as any).server.indent;
-
-
 export interface StartSessionOptions {
 	readonly globalPlugins: ts.server.SessionOptions['globalPlugins'];
 	readonly pluginProbeLocations: ts.server.SessionOptions['pluginProbeLocations'];
@@ -25,98 +22,103 @@ export interface StartSessionOptions {
 	readonly disableAutomaticTypingAcquisition: boolean;
 }
 
-export class WorkerSession extends ts.server.Session<{}> {
-
-	readonly wasmCancellationToken: WasmCancellationToken;
-	readonly listener: (message: any) => void;
-
-	constructor(
-		host: ts.server.ServerHost,
-		fs: FileSystem | undefined,
-		options: StartSessionOptions,
-		private readonly port: MessagePort,
-		pathMapper: PathMapper,
-		logger: Logger
-	) {
-		const cancellationToken = new WasmCancellationToken();
-		const typingsInstaller = options.disableAutomaticTypingAcquisition || !fs ? ts.server.nullTypingsInstaller : new WebTypingsInstaller(host, '/vscode-global-typings/ts-nul-authority/projects');
-
-		super({
-			host,
-			cancellationToken,
-			...options,
-			typingsInstaller,
-			byteLength: () => { throw new Error('Not implemented'); }, // Formats the message text in send of Session which is overridden in this class so not needed
-			hrtime,
-			logger: logger.tsLogger,
-			canUseEvents: true,
-		});
-		this.wasmCancellationToken = cancellationToken;
-
-		this.listener = (message: any) => {
-			// TEMP fix since Cancellation.retrieveCheck is not correct
-			function retrieveCheck2(data: any) {
-				if (!globalThis.crossOriginIsolated || !(data.$cancellationData instanceof SharedArrayBuffer)) {
-					return () => false;
+export function createWorkerSession(
+	ts: typeof import('typescript/lib/tsserverlibrary'),
+	host: ts.server.ServerHost,
+	fs: FileSystem | undefined,
+	options: StartSessionOptions,
+	port: MessagePort,
+	pathMapper: PathMapper,
+	logger: Logger,
+) {
+	const indent: (str: string) => string = (ts as any).server.indent;
+
+	return new class WorkerSession extends ts.server.Session<{}> {
+
+		private readonly wasmCancellationToken: WasmCancellationToken;
+		private readonly listener: (message: any) => void;
+
+		constructor() {
+			const cancellationToken = new WasmCancellationToken();
+			const typingsInstaller = options.disableAutomaticTypingAcquisition || !fs ? ts.server.nullTypingsInstaller : new WebTypingsInstallerClient(host, '/vscode-global-typings/ts-nul-authority/projects');
+
+			super({
+				host,
+				cancellationToken,
+				...options,
+				typingsInstaller,
+				byteLength: () => { throw new Error('Not implemented'); }, // Formats the message text in send of Session which is overridden in this class so not needed
+				hrtime,
+				logger: logger.tsLogger,
+				canUseEvents: true,
+			});
+			this.wasmCancellationToken = cancellationToken;
+
+			this.listener = (message: any) => {
+				// TEMP fix since Cancellation.retrieveCheck is not correct
+				function retrieveCheck2(data: any) {
+					if (!globalThis.crossOriginIsolated || !(data.$cancellationData instanceof SharedArrayBuffer)) {
+						return () => false;
+					}
+					const typedArray = new Int32Array(data.$cancellationData, 0, 1);
+					return () => {
+						return Atomics.load(typedArray, 0) === 1;
+					};
 				}
-				const typedArray = new Int32Array(data.$cancellationData, 0, 1);
-				return () => {
-					return Atomics.load(typedArray, 0) === 1;
-				};
-			}
 
-			const shouldCancel = retrieveCheck2(message.data);
-			if (shouldCancel) {
-				this.wasmCancellationToken.shouldCancel = shouldCancel;
-			}
+				const shouldCancel = retrieveCheck2(message.data);
+				if (shouldCancel) {
+					this.wasmCancellationToken.shouldCancel = shouldCancel;
+				}
 
-			try {
-				if (message.data.command === 'updateOpen') {
-					const args = message.data.arguments as ts.server.protocol.UpdateOpenRequestArgs;
-					for (const open of args.openFiles ?? []) {
-						if (open.projectRootPath) {
-							pathMapper.addProjectRoot(open.projectRootPath);
+				try {
+					if (message.data.command === 'updateOpen') {
+						const args = message.data.arguments as ts.server.protocol.UpdateOpenRequestArgs;
+						for (const open of args.openFiles ?? []) {
+							if (open.projectRootPath) {
+								pathMapper.addProjectRoot(open.projectRootPath);
+							}
 						}
 					}
+				} catch {
+					// Noop
 				}
-			} catch {
-				// Noop
-			}
 
-			this.onMessage(message.data);
-		};
-	}
+				this.onMessage(message.data);
+			};
+		}
 
-	public override send(msg: ts.server.protocol.Message) {
-		if (msg.type === 'event' && !this.canUseEvents) {
+		public override send(msg: ts.server.protocol.Message) {
+			if (msg.type === 'event' && !this.canUseEvents) {
+				if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
+					this.logger.info(`Session does not support events: ignored event: ${JSON.stringify(msg)}`);
+				}
+				return;
+			}
 			if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
-				this.logger.info(`Session does not support events: ignored event: ${JSON.stringify(msg)}`);
+				this.logger.info(`${msg.type}:${indent(JSON.stringify(msg))}`);
 			}
-			return;
-		}
-		if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
-			this.logger.info(`${msg.type}:${indent(JSON.stringify(msg))}`);
+			port.postMessage(msg);
 		}
-		this.port.postMessage(msg);
-	}
 
-	protected override parseMessage(message: {}): ts.server.protocol.Request {
-		return message as ts.server.protocol.Request;
-	}
+		protected override parseMessage(message: {}): ts.server.protocol.Request {
+			return message as ts.server.protocol.Request;
+		}
 
-	protected override toStringMessage(message: {}) {
-		return JSON.stringify(message, undefined, 2);
-	}
+		protected override toStringMessage(message: {}) {
+			return JSON.stringify(message, undefined, 2);
+		}
 
-	override exit() {
-		this.logger.info('Exiting...');
-		this.port.removeEventListener('message', this.listener);
-		this.projectService.closeLog();
-		close();
-	}
+		override exit() {
+			this.logger.info('Exiting...');
+			port.removeEventListener('message', this.listener);
+			this.projectService.closeLog();
+			close();
+		}
 
-	listen() {
-		this.logger.info(`webServer.ts: tsserver starting to listen for messages on 'message'...`);
-		this.port.onmessage = this.listener;
-	}
+		listen() {
+			this.logger.info(`webServer.ts: tsserver starting to listen for messages on 'message'...`);
+			port.onmessage = this.listener;
+		}
+	}();
 }