src/misc: fix strict type errors in various commands and data providers

For golang/vscode-go#57.

Change-Id: I18d3ea64f3022ee1ca722ace8d03ec8bbd51fe26
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/401620
Run-TryBot: Jamal Carvalho <jamal@golang.org>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/goBuild.ts b/src/goBuild.ts
index b1bc108..fdcfea6 100644
--- a/src/goBuild.ts
+++ b/src/goBuild.ts
@@ -41,7 +41,7 @@
 		}
 	}
 
-	const documentUri = editor ? editor.document.uri : null;
+	const documentUri = editor?.document.uri;
 	const goConfig = getGoConfig(documentUri);
 
 	outputChannel.clear(); // Ensures stale output from build on save is cleared
@@ -51,7 +51,7 @@
 	isModSupported(documentUri).then((isMod) => {
 		goBuild(documentUri, isMod, goConfig, buildWorkspace)
 			.then((errors) => {
-				handleDiagnosticErrors(editor ? editor.document : null, errors, buildDiagnosticCollection);
+				handleDiagnosticErrors(editor?.document, errors, buildDiagnosticCollection);
 				diagnosticsStatusBarItem.hide();
 			})
 			.catch((err) => {
@@ -70,7 +70,7 @@
  * @param buildWorkspace If true builds code in all workspace.
  */
 export async function goBuild(
-	fileUri: vscode.Uri,
+	fileUri: vscode.Uri | undefined,
 	isMod: boolean,
 	goConfig: vscode.WorkspaceConfiguration,
 	buildWorkspace?: boolean
@@ -91,13 +91,14 @@
 	};
 
 	const currentWorkspace = getWorkspaceFolderPath(fileUri);
-	const cwd = buildWorkspace && currentWorkspace ? currentWorkspace : path.dirname(fileUri.fsPath);
+	const cwd = buildWorkspace && currentWorkspace ? currentWorkspace : path.dirname(fileUri?.fsPath ?? '');
 	if (!path.isAbsolute(cwd)) {
 		return Promise.resolve([]);
 	}
 
 	// Skip building if cwd is in the module cache
-	if (isMod && cwd.startsWith(getModuleCache())) {
+	const cache = getModuleCache();
+	if (isMod && cache && cwd.startsWith(cache)) {
 		return [];
 	}
 
@@ -133,7 +134,7 @@
 				currentWorkspace,
 				'error',
 				true,
-				null,
+				'',
 				buildEnv,
 				true,
 				tokenSource.token
@@ -162,10 +163,10 @@
 	running = true;
 	return runTool(
 		buildArgs.concat('-o', tmpPath, importPath),
-		cwd,
+		cwd ?? '',
 		'error',
 		true,
-		null,
+		'',
 		buildEnv,
 		true,
 		tokenSource.token
diff --git a/src/goCover.ts b/src/goCover.ts
index b645f09..d2f8bcc 100644
--- a/src/goCover.ts
+++ b/src/goCover.ts
@@ -213,7 +213,7 @@
  * @param packageDirPath Absolute path of the package for which the coverage was calculated
  * @param dir Directory to execute go list in
  */
-export function applyCodeCoverageToAllEditors(coverProfilePath: string, dir: string): Promise<void> {
+export function applyCodeCoverageToAllEditors(coverProfilePath: string, dir?: string): Promise<void> {
 	const v = new Promise<void>((resolve, reject) => {
 		try {
 			const showCounts = getGoConfig().get('coverShowCounts') as boolean;
@@ -285,7 +285,7 @@
 				resolve();
 			});
 		} catch (e) {
-			vscode.window.showInformationMessage(e.msg);
+			vscode.window.showInformationMessage((e as any).msg);
 			reject(e);
 		}
 	});
@@ -358,7 +358,7 @@
  * Apply the code coverage highlighting in given editor
  * @param editor
  */
-export function applyCodeCoverage(editor: vscode.TextEditor) {
+export function applyCodeCoverage(editor: vscode.TextEditor | undefined) {
 	if (!editor || editor.document.languageId !== 'go' || editor.document.fileName.endsWith('_test.go')) {
 		return;
 	}
diff --git a/src/goDebugConfiguration.ts b/src/goDebugConfiguration.ts
index 97fafeb..8e5f346 100644
--- a/src/goDebugConfiguration.ts
+++ b/src/goDebugConfiguration.ts
@@ -121,7 +121,7 @@
 		folder: vscode.WorkspaceFolder | undefined,
 		debugConfiguration: vscode.DebugConfiguration,
 		token?: vscode.CancellationToken
-	): Promise<vscode.DebugConfiguration> {
+	): Promise<vscode.DebugConfiguration | undefined> {
 		const activeEditor = vscode.window.activeTextEditor;
 		if (!debugConfiguration || !debugConfiguration.request) {
 			// if 'request' is missing interpret this as a missing launch.json
@@ -155,14 +155,14 @@
 
 		const goConfig = getGoConfig(folder && folder.uri);
 		const dlvConfig = goConfig['delveConfig'];
-		const defaultConfig = vscode.extensions.getExtension(extensionId).packageJSON.contributes.configuration
+		const defaultConfig = vscode.extensions.getExtension(extensionId)?.packageJSON.contributes.configuration
 			.properties['go.delveConfig'].properties;
 
 		// Figure out which debugAdapter is being used first, so we can use this to send warnings
 		// for properties that don't apply.
 		// If debugAdapter is not provided in launch.json, see if it's in settings.json.
 		if (!debugConfiguration.hasOwnProperty('debugAdapter') && dlvConfig.hasOwnProperty('debugAdapter')) {
-			const { globalValue, workspaceValue } = goConfig.inspect('delveConfig.debugAdapter');
+			const { globalValue, workspaceValue } = goConfig.inspect('delveConfig.debugAdapter') ?? {};
 			// user configured the default debug adapter through settings.json.
 			if (globalValue !== undefined || workspaceValue !== undefined) {
 				debugConfiguration['debugAdapter'] = dlvConfig['debugAdapter'];
@@ -206,8 +206,8 @@
 		if (
 			debugAdapter === 'dlv-dap' &&
 			(debugConfiguration.hasOwnProperty('dlvLoadConfig') ||
-				goConfig.inspect('delveConfig.dlvLoadConfig').globalValue !== undefined ||
-				goConfig.inspect('delveConfig.dlvLoadConfig').workspaceValue !== undefined)
+				goConfig.inspect('delveConfig.dlvLoadConfig')?.globalValue !== undefined ||
+				goConfig.inspect('delveConfig.dlvLoadConfig')?.workspaceValue !== undefined)
 		) {
 			this.showWarning(
 				'ignoreDebugDlvConfigWithDlvDapWarning',
@@ -240,7 +240,7 @@
 
 		if (debugAdapter !== 'dlv-dap' && debugConfiguration.request === 'attach' && !debugConfiguration['cwd']) {
 			debugConfiguration['cwd'] = '${workspaceFolder}';
-			if (vscode.workspace.workspaceFolders?.length > 1) {
+			if (vscode.workspace.workspaceFolders?.length ?? 0 > 1) {
 				debugConfiguration['cwd'] = '${fileWorkspaceFolder}';
 			}
 		}
@@ -394,7 +394,7 @@
 		folder: vscode.WorkspaceFolder | undefined,
 		debugConfiguration: vscode.DebugConfiguration,
 		token?: vscode.CancellationToken
-	): vscode.DebugConfiguration {
+	): vscode.DebugConfiguration | null {
 		const debugAdapter = debugConfiguration['debugAdapter'];
 		if (debugAdapter === '') {
 			return null;
diff --git a/src/goDebugFactory.ts b/src/goDebugFactory.ts
index 4ea35bf..fe83cfe 100644
--- a/src/goDebugFactory.ts
+++ b/src/goDebugFactory.ts
@@ -218,13 +218,13 @@
 // VSCode and a dlv dap process spawned and managed by this adapter.
 // It turns the process's stdout/stderrr into OutputEvent.
 export class DelveDAPOutputAdapter extends ProxyDebugAdapter {
-	constructor(private configuration: vscode.DebugConfiguration, logger?: Logger) {
+	constructor(private configuration: vscode.DebugConfiguration, logger: Logger) {
 		super(logger);
 	}
 
-	private connected: Promise<{ connected: boolean; reason?: any }>;
-	private dlvDapServer: ChildProcess;
-	private socket: net.Socket;
+	private connected?: Promise<{ connected: boolean; reason?: any }>;
+	private dlvDapServer?: ChildProcess;
+	private socket?: net.Socket;
 	private terminatedOnError = false;
 
 	protected sendMessageToClient(message: vscode.DebugProtocolMessage) {
@@ -290,9 +290,6 @@
 		}
 		this.connected = undefined;
 
-		if (timeoutMS === undefined || timeoutMS < 0) {
-			timeoutMS = 1_000;
-		}
 		const dlvDapServer = this.dlvDapServer;
 		this.dlvDapServer = undefined;
 		if (!dlvDapServer) {
@@ -305,6 +302,9 @@
 			return;
 		}
 		await new Promise<void>((resolve) => {
+			if (timeoutMS === undefined || timeoutMS < 0) {
+				timeoutMS = 1_000;
+			}
 			const exitTimeoutToken = setTimeout(() => {
 				this.logger?.error(`dlv dap process (${dlvDapServer.pid}) isn't responding. Killing...`);
 				dlvDapServer.kill('SIGINT'); // Don't use treekill but let dlv handle cleaning up the child processes.
@@ -341,7 +341,7 @@
 
 	async startDapServer(
 		configuration: vscode.DebugConfiguration
-	): Promise<{ dlvDapServer?: ChildProcessWithoutNullStreams; socket: net.Socket }> {
+	): Promise<{ dlvDapServer?: ChildProcess; socket: net.Socket }> {
 		const log = (msg: string) => this.outputEvent('stdout', msg);
 		const logErr = (msg: string) => this.outputEvent('stderr', msg);
 		const logConsole = (msg: string) => {
@@ -450,7 +450,8 @@
 
 function waitForDAPServer(port: number, timeoutMs: number): Promise<net.Socket> {
 	return new Promise((resolve, reject) => {
-		let s: net.Server = undefined;
+		// eslint-disable-next-line prefer-const
+		let s: net.Server | undefined;
 		const timeoutToken = setTimeout(() => {
 			if (s?.listening) {
 				s.close();
@@ -463,7 +464,7 @@
 				`connected: ${port} (remote: ${socket.remoteAddress}:${socket.remotePort} local: ${socket.localAddress}:${socket.localPort})`
 			);
 			clearTimeout(timeoutToken);
-			s.close(); // accept no more connection
+			s?.close(); // accept no more connection
 			socket.resume();
 			resolve(socket);
 		});
diff --git a/src/goDeveloperSurvey.ts b/src/goDeveloperSurvey.ts
index a18e9c1..ec942e8 100644
--- a/src/goDeveloperSurvey.ts
+++ b/src/goDeveloperSurvey.ts
@@ -58,7 +58,7 @@
 			setTimeout(callback, 5 * timeMinute);
 			return;
 		}
-		cfg = await promptForDeveloperSurvey(cfg, now);
+		cfg = await promptForDeveloperSurvey(cfg ?? {}, now);
 		if (cfg) {
 			flushSurveyConfig(developerSurveyConfig, cfg);
 		}
@@ -69,7 +69,7 @@
 // shouldPromptForSurvey decides if we should prompt the given user to take the
 // survey. It returns the DeveloperSurveyConfig if we should prompt, and
 // undefined if we should not prompt.
-export function shouldPromptForSurvey(now: Date, cfg: DeveloperSurveyConfig): DeveloperSurveyConfig {
+export function shouldPromptForSurvey(now: Date, cfg: DeveloperSurveyConfig): DeveloperSurveyConfig | undefined {
 	// TODO(rstambler): Merge checks for surveys into a setting.
 
 	// Don't prompt if the survey hasn't started or is over.
diff --git a/src/goEnv.ts b/src/goEnv.ts
index 59a7565..aebd903 100644
--- a/src/goEnv.ts
+++ b/src/goEnv.ts
@@ -36,7 +36,7 @@
 					break;
 			}
 		});
-		return;
+		return {};
 	}
 	env['GOPATH'] = toolsGopath;
 
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index 466f7c4..7328326 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -38,7 +38,7 @@
 	}
 }
 
-export let terminalCreationListener: vscode.Disposable;
+export let terminalCreationListener: vscode.Disposable | undefined;
 
 let environmentVariableCollection: vscode.EnvironmentVariableCollection;
 export function setEnvironmentVariableCollection(env: vscode.EnvironmentVariableCollection) {
@@ -79,7 +79,7 @@
 	}
 
 	// fetch default go and uninstalled go versions
-	let defaultOption: GoEnvironmentOption;
+	let defaultOption: GoEnvironmentOption | undefined;
 	let uninstalledOptions: GoEnvironmentOption[];
 	let goSDKOptions: GoEnvironmentOption[];
 	try {
@@ -89,7 +89,7 @@
 			getSDKGoOptions()
 		]);
 	} catch (e) {
-		vscode.window.showErrorMessage(e.message);
+		vscode.window.showErrorMessage((e as Error).message);
 		return;
 	}
 
@@ -123,7 +123,7 @@
 	try {
 		await setSelectedGo(selection);
 	} catch (e) {
-		vscode.window.showErrorMessage(e.message);
+		vscode.window.showErrorMessage((e as Error).message);
 	}
 }
 
@@ -163,7 +163,7 @@
 			return false;
 		}
 		const newGoBin = fixDriveCasingInWindows(newGoUris[0].fsPath);
-		const oldGoBin = fixDriveCasingInWindows(path.join(defaultUri.fsPath, correctBinname('go')));
+		const oldGoBin = fixDriveCasingInWindows(path.join(defaultUri?.fsPath ?? '', correctBinname('go')));
 
 		if (newGoBin === oldGoBin) {
 			return false;
@@ -472,7 +472,7 @@
 		const dlPath = `golang.org/dl/${result.version}`;
 		const label = result.version.replace('go', 'Go ');
 		return [...opts, new GoEnvironmentOption(dlPath, label, false)];
-	}, []);
+	}, [] as GoEnvironmentOption[]);
 }
 
 export const latestGoVersionKey = 'latestGoVersions';
diff --git a/src/goExplorer.ts b/src/goExplorer.ts
index b8d503f..8423cc6 100644
--- a/src/goExplorer.ts
+++ b/src/goExplorer.ts
@@ -99,7 +99,7 @@
 		if (!uri) {
 			return;
 		}
-		let pick: { label?: string; description?: string };
+		let pick: { label?: string; description?: string } | undefined;
 		if (isEnvTreeItem(item)) {
 			pick = { label: item.key, description: item.value };
 		} else {
@@ -114,7 +114,7 @@
 		if (!pick) return;
 		const { label, description } = pick;
 		const value = await vscode.window.showInputBox({ title: label, value: description });
-		if (typeof value !== 'undefined') {
+		if (label && typeof value !== 'undefined') {
 			await GoEnv.edit({ [label]: value });
 		}
 	}
@@ -140,7 +140,7 @@
 	}
 
 	private async envTreeItems(uri?: vscode.Uri) {
-		const env = await this.goEnvCache.get(uri?.toString());
+		const env = await this.goEnvCache.get(uri?.toString() ?? '');
 		const items = [];
 		for (const [k, v] of Object.entries(env)) {
 			if (v !== '') {
@@ -270,9 +270,9 @@
 	contextValue = 'go:explorer:toolitem';
 	description = 'not installed';
 	label: string;
-	children: vscode.TreeItem[];
+	children?: vscode.TreeItem[];
 	collapsibleState?: vscode.TreeItemCollapsibleState;
-	tooltip: string;
+	tooltip?: string;
 	constructor({ name, version, goVersion, binPath, error }: ToolDetail) {
 		this.label = name;
 		if (binPath) {
@@ -314,7 +314,7 @@
 			version: moduleVersion
 		};
 	} catch (e) {
-		return { name: name, error: e };
+		return { name: name, error: e as Error };
 	}
 }
 
diff --git a/src/goFillStruct.ts b/src/goFillStruct.ts
index e4bc1c2..9a2f787 100644
--- a/src/goFillStruct.ts
+++ b/src/goFillStruct.ts
@@ -99,7 +99,7 @@
 			}
 		});
 		if (p.pid) {
-			p.stdin.end(input);
+			p.stdin?.end(input);
 		}
 	});
 }
diff --git a/src/goGenerateTests.ts b/src/goGenerateTests.ts
index b5e02f7..756dca8 100644
--- a/src/goGenerateTests.ts
+++ b/src/goGenerateTests.ts
@@ -69,10 +69,10 @@
 	vscode.commands.executeCommand('vscode.open', vscode.Uri.file(targetFilePath));
 }
 
-export function generateTestCurrentPackage(): Promise<boolean> {
+export async function generateTestCurrentPackage(): Promise<boolean> {
 	const editor = checkActiveEditor();
 	if (!editor) {
-		return;
+		return false;
 	}
 	return generateTests(
 		{
@@ -83,10 +83,10 @@
 	);
 }
 
-export function generateTestCurrentFile(): Promise<boolean> {
+export async function generateTestCurrentFile(): Promise<boolean> {
 	const editor = checkActiveEditor();
 	if (!editor) {
-		return;
+		return false;
 	}
 
 	return generateTests(
@@ -101,14 +101,12 @@
 export async function generateTestCurrentFunction(): Promise<boolean> {
 	const editor = checkActiveEditor();
 	if (!editor) {
-		return;
+		return false;
 	}
 
 	const functions = await getFunctions(editor.document);
 	const selection = editor.selection;
-	const currentFunction: vscode.DocumentSymbol = functions.find(
-		(func) => selection && func.range.contains(selection.start)
-	);
+	const currentFunction = functions.find((func) => selection && func.range.contains(selection.start));
 
 	if (!currentFunction) {
 		vscode.window.showInformationMessage('No function found at cursor.');
@@ -216,8 +214,8 @@
 
 				return resolve(true);
 			} catch (e) {
-				vscode.window.showInformationMessage(e.msg);
-				outputChannel.append(e.msg);
+				vscode.window.showInformationMessage((e as any).msg);
+				outputChannel.append((e as any).msg);
 				reject(e);
 			}
 		});
@@ -226,7 +224,7 @@
 
 async function getFunctions(doc: vscode.TextDocument): Promise<vscode.DocumentSymbol[]> {
 	const documentSymbolProvider = new GoDocumentSymbolProvider();
-	const symbols = await documentSymbolProvider.provideDocumentSymbols(doc, null);
+	const symbols = await documentSymbolProvider.provideDocumentSymbols(doc);
 	return symbols[0].children.filter((sym) =>
 		[vscode.SymbolKind.Function, vscode.SymbolKind.Method].includes(sym.kind)
 	);
diff --git a/src/goGetPackage.ts b/src/goGetPackage.ts
index 86e5dc9..6e8ce30 100644
--- a/src/goGetPackage.ts
+++ b/src/goGetPackage.ts
@@ -14,9 +14,8 @@
 
 export function goGetPackage() {
 	const editor = vscode.window.activeTextEditor;
-	const selection = editor.selection;
-	const selectedText = editor.document.lineAt(selection.active.line).text;
-
+	const selection = editor?.selection;
+	const selectedText = editor?.document.lineAt(selection?.active.line ?? 0).text ?? '';
 	const importPath = getImportPath(selectedText);
 	if (importPath === '') {
 		vscode.window.showErrorMessage('No import path to get');
diff --git a/src/goImpl.ts b/src/goImpl.ts
index c9c59f4..dfb7870 100644
--- a/src/goImpl.ts
+++ b/src/goImpl.ts
@@ -70,6 +70,6 @@
 		}
 	);
 	if (p.pid) {
-		p.stdin.end();
+		p.stdin?.end();
 	}
 }
diff --git a/src/goImport.ts b/src/goImport.ts
index 4a7a062..43d728e 100644
--- a/src/goImport.ts
+++ b/src/goImport.ts
@@ -26,7 +26,9 @@
 		excludeImportedPkgs && vscode.window.activeTextEditor
 			? await getImports(vscode.window.activeTextEditor.document)
 			: [];
-	const pkgMap = await getImportablePackages(vscode.window.activeTextEditor.document.fileName, true);
+	const pkgMap = vscode.window.activeTextEditor
+		? await getImportablePackages(vscode.window.activeTextEditor?.document.fileName, true)
+		: new Map();
 	const stdLibs: string[] = [];
 	const nonStdLibs: string[] = [];
 	pkgMap.forEach((value, key) => {
@@ -46,9 +48,12 @@
 	const COMMAND = 'gopls.list_known_packages';
 	if (languageClient && serverInfo?.Commands?.includes(COMMAND)) {
 		try {
-			const uri = languageClient.code2ProtocolConverter.asTextDocumentIdentifier(
-				vscode.window.activeTextEditor.document
-			).uri;
+			const editor = vscode.window.activeTextEditor;
+			if (!editor) {
+				vscode.window.showErrorMessage('No active editor found.');
+				return [];
+			}
+			const uri = languageClient.code2ProtocolConverter.asTextDocumentIdentifier(editor.document).uri;
 			const params: ExecuteCommandParams = {
 				command: COMMAND,
 				arguments: [
@@ -80,7 +85,7 @@
 		importsOption: GoOutlineImportsOptions.Only,
 		document
 	};
-	const symbols = await documentSymbols(options, null);
+	const symbols = await documentSymbols(options);
 	if (!symbols || !symbols.length) {
 		return [];
 	}
@@ -102,13 +107,18 @@
 	}
 }
 
-export function getTextEditForAddImport(arg: string): vscode.TextEdit[] {
+export function getTextEditForAddImport(arg: string | undefined): vscode.TextEdit[] | undefined {
 	// Import name wasn't provided
 	if (arg === undefined) {
-		return null;
+		return undefined;
+	}
+	const editor = vscode.window.activeTextEditor;
+	if (!editor) {
+		vscode.window.showErrorMessage('No active editor found.');
+		return [];
 	}
 
-	const { imports, pkg } = parseFilePrelude(vscode.window.activeTextEditor.document.getText());
+	const { imports, pkg } = parseFilePrelude(editor.document.getText());
 	if (imports.some((block) => block.pkgs.some((pkgpath) => pkgpath === arg))) {
 		return [];
 	}
@@ -131,7 +141,7 @@
 
 		edits.push(vscode.TextEdit.insert(new vscode.Position(minusCgo[0].start, 0), 'import (\n\t"' + arg + '"\n'));
 		minusCgo.forEach((element) => {
-			const currentLine = vscode.window.activeTextEditor.document.lineAt(element.start).text;
+			const currentLine = editor.document.lineAt(element.start).text;
 			const updatedLine = currentLine.replace(/^\s*import\s*/, '\t');
 			edits.push(
 				vscode.TextEdit.replace(
@@ -167,9 +177,12 @@
 		const COMMAND = 'gopls.add_import';
 		if (languageClient && serverInfo?.Commands?.includes(COMMAND)) {
 			try {
-				const uri = languageClient.code2ProtocolConverter.asTextDocumentIdentifier(
-					vscode.window.activeTextEditor.document
-				).uri;
+				const editor = vscode.window.activeTextEditor;
+				if (!editor) {
+					vscode.window.showErrorMessage('No active editor found to determine current package.');
+					return [];
+				}
+				const uri = languageClient.code2ProtocolConverter.asTextDocumentIdentifier(editor.document).uri;
 				const params: ExecuteCommandParams = {
 					command: COMMAND,
 					arguments: [
diff --git a/src/goInstall.ts b/src/goInstall.ts
index 71a19b5..aecf947 100644
--- a/src/goInstall.ts
+++ b/src/goInstall.ts
@@ -39,7 +39,8 @@
 	const isMod = await isModSupported(editor.document.uri);
 
 	// Skip installing if cwd is in the module cache
-	if (isMod && cwd.startsWith(getModuleCache())) {
+	const cache = getModuleCache();
+	if (isMod && cache && cwd.startsWith(cache)) {
 		return;
 	}
 
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index e713930..b8b5853 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -56,7 +56,7 @@
 	let allTools = getConfiguredTools(goVersion, getGoConfig(), getGoplsConfig());
 
 	// exclude tools replaced by alternateTools.
-	const alternateTools: { [key: string]: string } = getGoConfig().get('alternateTools');
+	const alternateTools: { [key: string]: string } = getGoConfig().get('alternateTools') ?? {};
 	allTools = allTools.filter((tool) => {
 		return !alternateTools[tool.name];
 	});
@@ -154,7 +154,7 @@
 		installingMsg += `the configured GOBIN: ${envForTools['GOBIN']}`;
 	} else {
 		const p = toolsGopath
-			.split(path.delimiter)
+			?.split(path.delimiter)
 			.map((e) => path.join(e, 'bin'))
 			.join(path.delimiter);
 		installingMsg += `${p}`;
@@ -164,7 +164,7 @@
 	// This ensures that users get the latest tagged version, rather than master,
 	// which may be unstable.
 	let modulesOff = false;
-	if (goVersion.lt('1.11')) {
+	if (goVersion?.lt('1.11')) {
 		modulesOff = true;
 	} else {
 		installingMsg += ' in module mode.';
@@ -225,7 +225,7 @@
 }
 
 // installTool installs the specified tool.
-export async function installTool(tool: ToolAtVersion): Promise<string> {
+export async function installTool(tool: ToolAtVersion): Promise<string | undefined> {
 	const goVersion = await getGoForInstall(await getGoVersion());
 	const envForTools = toolInstallationEnvironment();
 
@@ -237,7 +237,7 @@
 	goVersion: GoVersion, // go version to be used for installation.
 	envForTools: NodeJS.Dict<string>,
 	modulesOn: boolean
-): Promise<string> {
+): Promise<string | undefined> {
 	// Some tools may have to be closed before we reinstall them.
 	if (tool.close) {
 		const reason = await tool.close(envForTools);
@@ -253,7 +253,7 @@
 	if (!modulesOn) {
 		importPath = getImportPath(tool, goVersion);
 	} else {
-		let version: semver.SemVer | string | undefined = tool.version;
+		let version: semver.SemVer | string | undefined | null = tool.version;
 		if (!version) {
 			if (tool.usePrereleaseInPreviewMode && extensionInfo.isPreview) {
 				version = await latestToolVersion(tool, true);
@@ -265,7 +265,7 @@
 	}
 
 	try {
-		if (!modulesOn || goVersion.lt('1.16') || hasModSuffix(tool)) {
+		if (!modulesOn || goVersion?.lt('1.16') || hasModSuffix(tool)) {
 			await installToolWithGoGet(tool, goVersion, env, modulesOn, importPath);
 		} else {
 			await installToolWithGoInstall(goVersion, env, importPath);
@@ -282,7 +282,7 @@
 async function installToolWithGoInstall(goVersion: GoVersion, env: NodeJS.Dict<string>, importPath: string) {
 	// Unlike installToolWithGoGet, `go install` in module mode
 	// can run in the current directory safely. So, use the user-specified go tool path.
-	const goBinary = goVersion.binaryPath || getBinPath('go');
+	const goBinary = goVersion?.binaryPath || getBinPath('go');
 	const opts = {
 		env,
 		cwd: getWorkspaceFolderPath()
@@ -306,7 +306,11 @@
 	// (which can be a wrapper script that switches 'go').
 	const goBinary = getCurrentGoRoot()
 		? path.join(getCurrentGoRoot(), 'bin', correctBinname('go'))
-		: goVersion.binaryPath;
+		: goVersion?.binaryPath;
+	if (!goBinary) {
+		vscode.window.showErrorMessage('Go binary not found.');
+		return;
+	}
 
 	// Build the arguments list for the tool installation.
 	const args = ['get', '-x'];
@@ -332,6 +336,7 @@
 		env,
 		cwd: toolsTmpDir
 	};
+
 	try {
 		const execFile = util.promisify(cp.execFile);
 		logVerbose(`$ ${goBinary} ${args.join(' ')} (cwd: ${opts.cwd})`);
@@ -633,16 +638,16 @@
 
 function getMissingTools(goVersion: GoVersion): Promise<Tool[]> {
 	const keys = getConfiguredTools(goVersion, getGoConfig(), getGoplsConfig());
-	return Promise.all<Tool>(
+	return Promise.all(
 		keys.map(
 			(tool) =>
-				new Promise<Tool>((resolve, reject) => {
+				new Promise<Tool | null>((resolve, reject) => {
 					const toolPath = getBinPath(tool.name);
 					resolve(path.isAbsolute(toolPath) ? null : tool);
 				})
 		)
 	).then((res) => {
-		return res.filter((x) => x != null);
+		return res.filter((x): x is Tool => x != null);
 	});
 }
 
@@ -741,7 +746,7 @@
 			...
 		*/
 		const lines = stdout.split('\n', 3);
-		const goVersion = lines[0] && lines[0].match(/\s+(go\d+.\d+\S*)/)[1];
+		const goVersion = lines[0] && lines[0].match(/\s+(go\d+.\d+\S*)/)?.[1];
 		const moduleVersion = lines[2].split(/\s+/)[3];
 		return { goVersion, moduleVersion };
 	} catch (e) {
diff --git a/src/goLint.ts b/src/goLint.ts
index 269763c..db1171c 100644
--- a/src/goLint.ts
+++ b/src/goLint.ts
@@ -29,7 +29,7 @@
 		}
 	}
 
-	const documentUri = editor ? editor.document.uri : null;
+	const documentUri = editor ? editor.document.uri : undefined;
 	const goConfig = getGoConfig(documentUri);
 	const goplsConfig = getGoplsConfig(documentUri);
 
@@ -39,7 +39,7 @@
 
 	goLint(documentUri, goConfig, goplsConfig, scope)
 		.then((warnings) => {
-			handleDiagnosticErrors(editor ? editor.document : null, warnings, lintDiagnosticCollection);
+			handleDiagnosticErrors(editor ? editor.document : undefined, warnings, lintDiagnosticCollection);
 			diagnosticsStatusBarItem.hide();
 		})
 		.catch((err) => {
@@ -56,7 +56,7 @@
  * @param scope Scope in which to run the linter.
  */
 export function goLint(
-	fileUri: vscode.Uri,
+	fileUri: vscode.Uri | undefined,
 	goConfig: vscode.WorkspaceConfiguration,
 	goplsConfig: vscode.WorkspaceConfiguration,
 	scope?: string
@@ -78,7 +78,7 @@
 
 	const currentWorkspace = getWorkspaceFolderPath(fileUri);
 
-	const cwd = scope === 'workspace' && currentWorkspace ? currentWorkspace : path.dirname(fileUri.fsPath);
+	const cwd = scope === 'workspace' && currentWorkspace ? currentWorkspace : path.dirname(fileUri?.fsPath ?? '');
 
 	if (!path.isAbsolute(cwd)) {
 		return Promise.resolve([]);
@@ -128,8 +128,8 @@
 		args.push('./...');
 		outputChannel.appendLine(`Starting linting the current workspace at ${currentWorkspace}`);
 	} else if (scope === 'file') {
-		args.push(fileUri.fsPath);
-		outputChannel.appendLine(`Starting linting the current file at ${fileUri.fsPath}`);
+		args.push(fileUri?.fsPath ?? '');
+		outputChannel.appendLine(`Starting linting the current file at ${fileUri?.fsPath}`);
 	} else {
 		outputChannel.appendLine(`Starting linting the current package at ${cwd}`);
 	}
diff --git a/src/goMain.ts b/src/goMain.ts
index a8b44fb..22f06ec 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -123,7 +123,7 @@
 	return;
 };
 
-export async function activate(ctx: vscode.ExtensionContext): Promise<ExtensionAPI> {
+export async function activate(ctx: vscode.ExtensionContext): Promise<ExtensionAPI | undefined> {
 	if (process.env['VSCODE_GO_IN_TEST'] === '1') {
 		// Make sure this does not run when running in test.
 		return;
@@ -205,15 +205,11 @@
 	// TODO: let configureLanguageServer to return its status.
 	await configureLanguageServer(ctx);
 
-	if (
-		!languageServerIsRunning &&
-		vscode.window.activeTextEditor &&
-		vscode.window.activeTextEditor.document.languageId === 'go' &&
-		isGoPathSet()
-	) {
+	const activeDoc = vscode.window.activeTextEditor?.document;
+	if (!languageServerIsRunning && activeDoc?.languageId === 'go' && isGoPathSet()) {
 		// Check mod status so that cache is updated and then run build/lint/vet
-		isModSupported(vscode.window.activeTextEditor.document.uri).then(() => {
-			runBuilds(vscode.window.activeTextEditor.document, getGoConfig());
+		isModSupported(activeDoc.uri).then(() => {
+			runBuilds(activeDoc, getGoConfig());
 		});
 	}
 
@@ -305,7 +301,9 @@
 
 	ctx.subscriptions.push(
 		vscode.commands.registerCommand('go.fill.struct', () => {
-			runFillStruct(vscode.window.activeTextEditor);
+			if (vscode.window.activeTextEditor) {
+				runFillStruct(vscode.window.activeTextEditor);
+			}
 		})
 	);
 
@@ -697,7 +695,7 @@
 					}
 					applyCodeCoverageToAllEditors(
 						coverProfilePath,
-						getWorkspaceFolderPath(vscode.window.activeTextEditor.document.uri)
+						getWorkspaceFolderPath(vscode.window.activeTextEditor?.document.uri)
 					);
 				});
 		})
@@ -899,7 +897,7 @@
 			return;
 		})
 	);
-	return oldTools.filter((tool) => !!tool);
+	return oldTools.filter((tool): tool is Tool => !!tool);
 }
 
 async function suggestUpdates(ctx: vscode.ExtensionContext) {
@@ -1019,7 +1017,7 @@
 		outputChannel.appendLine(info);
 	});
 
-	let folders = vscode.workspace.workspaceFolders?.map((folder) => {
+	let folders = vscode.workspace.workspaceFolders?.map<{ name: string; path?: string }>((folder) => {
 		return { name: folder.name, path: folder.uri.fsPath };
 	});
 	if (!folders) {
@@ -1059,7 +1057,7 @@
 	if (goroot === currentGOROOT) {
 		return;
 	}
-	if (!(await dirExists(goroot))) {
+	if (!(await dirExists(goroot ?? ''))) {
 		vscode.window.showWarningMessage(`go.goroot setting is ignored. ${goroot} is not a valid GOROOT directory.`);
 		return;
 	}
diff --git a/src/goMode.ts b/src/goMode.ts
index 6ff429e..0e7629f 100644
--- a/src/goMode.ts
+++ b/src/goMode.ts
@@ -7,9 +7,14 @@
 
 import vscode = require('vscode');
 
-export const GO_MODE: vscode.DocumentFilter = { language: 'go', scheme: 'file' };
-export const GO_MOD_MODE: vscode.DocumentFilter = { language: 'go.mod', scheme: 'file' };
-export const GO_SUM_MODE: vscode.DocumentFilter = { language: 'go.sum', scheme: 'file' };
+interface Filter extends vscode.DocumentFilter {
+	language: string;
+	scheme: string;
+}
+
+export const GO_MODE: Filter = { language: 'go', scheme: 'file' };
+export const GO_MOD_MODE: Filter = { language: 'go.mod', scheme: 'file' };
+export const GO_SUM_MODE: Filter = { language: 'go.sum', scheme: 'file' };
 
 export function isGoFile(document: vscode.TextDocument): boolean {
 	if (
diff --git a/src/goModifytags.ts b/src/goModifytags.ts
index 8378c72..9ea6ea6 100644
--- a/src/goModifytags.ts
+++ b/src/goModifytags.ts
@@ -89,11 +89,11 @@
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showInformationMessage('No editor is active.');
-		return;
+		return [];
 	}
 	if (!editor.document.fileName.endsWith('.go')) {
 		vscode.window.showInformationMessage('Current file is not a Go file.');
-		return;
+		return [];
 	}
 	const args = ['-modified', '-file', editor.document.fileName, '-format', 'json'];
 	if (
@@ -113,7 +113,7 @@
 	return args;
 }
 
-function getTagsAndOptions(config: GoTagsConfig, commandArgs: GoTagsConfig): Thenable<string[]> {
+function getTagsAndOptions(config: GoTagsConfig, commandArgs: GoTagsConfig): Thenable<(string | undefined)[]> {
 	const tags = commandArgs && commandArgs.hasOwnProperty('tags') ? commandArgs['tags'] : config['tags'];
 	const options = commandArgs && commandArgs.hasOwnProperty('options') ? commandArgs['options'] : config['options'];
 	const promptForTags =
@@ -163,6 +163,9 @@
 function runGomodifytags(args: string[]) {
 	const gomodifytags = getBinPath('gomodifytags');
 	const editor = vscode.window.activeTextEditor;
+	if (!editor) {
+		return;
+	}
 	const input = getFileArchive(editor.document);
 	const p = cp.execFile(gomodifytags, args, { env: toolExecutionEnvironment() }, (err, stdout, stderr) => {
 		if (err && (<any>err).code === 'ENOENT') {
@@ -181,11 +184,11 @@
 			return;
 		}
 		const output = <GomodifytagsOutput>JSON.parse(stdout);
-		vscode.window.activeTextEditor.edit((editBuilder) => {
+		editor.edit((editBuilder) => {
 			editBuilder.replace(new vscode.Range(output.start - 1, 0, output.end, 0), output.lines.join('\n') + '\n');
 		});
 	});
 	if (p.pid) {
-		p.stdin.end(input);
+		p.stdin?.end(input);
 	}
 }
diff --git a/src/goModules.ts b/src/goModules.ts
index dcb770e..34d230e 100644
--- a/src/goModules.ts
+++ b/src/goModules.ts
@@ -18,7 +18,7 @@
 import { getFromGlobalState, updateGlobalState } from './stateUtils';
 import { getBinPath, getGoVersion, getModuleCache, getWorkspaceFolderPath } from './util';
 import { envPath, fixDriveCasingInWindows, getCurrentGoRoot } from './utils/pathUtils';
-export let GO111MODULE: string;
+export let GO111MODULE: string | undefined;
 
 export async function runGoEnv(uri?: vscode.Uri, envvars: string[] = []): Promise<any> {
 	const goExecutable = getBinPath('go');
@@ -38,28 +38,28 @@
 		}
 		return JSON.parse(stdout);
 	} catch (e) {
-		vscode.window.showErrorMessage(`Failed to run "go env ${args}": ${e.message}`);
+		vscode.window.showErrorMessage(`Failed to run "go env ${args}": ${(e as Error).message}`);
 		return {};
 	}
 }
 
-export function isModSupported(fileuri: vscode.Uri, isDir?: boolean): Promise<boolean> {
+export function isModSupported(fileuri?: vscode.Uri, isDir?: boolean): Promise<boolean> {
 	return getModFolderPath(fileuri, isDir).then((modPath) => !!modPath);
 }
 
 export const packagePathToGoModPathMap: { [key: string]: string } = {};
 
-export async function getModFolderPath(fileuri: vscode.Uri, isDir?: boolean): Promise<string> {
-	const pkgUri = isDir ? fileuri : vscodeUri.Utils.dirname(fileuri);
-	const pkgPath = pkgUri.fsPath;
-	if (packagePathToGoModPathMap[pkgPath]) {
+export async function getModFolderPath(fileuri?: vscode.Uri, isDir?: boolean): Promise<string | undefined> {
+	const pkgUri = isDir ? fileuri : fileuri && vscodeUri.Utils.dirname(fileuri);
+	const pkgPath = pkgUri?.fsPath ?? '';
+	if (pkgPath && packagePathToGoModPathMap[pkgPath]) {
 		return packagePathToGoModPathMap[pkgPath];
 	}
 
 	// We never would be using the path under module cache for anything
 	// So, dont bother finding where exactly is the go.mod file
 	const moduleCache = getModuleCache();
-	if (fixDriveCasingInWindows(fileuri.fsPath).startsWith(moduleCache)) {
+	if (moduleCache && fixDriveCasingInWindows(fileuri?.fsPath ?? '').startsWith(moduleCache)) {
 		return moduleCache;
 	}
 	const goVersion = await getGoVersion();
@@ -74,7 +74,7 @@
 		goModEnvResult = path.dirname(goModEnvResult);
 		const goConfig = getGoConfig(fileuri);
 
-		if (goConfig['inferGopath'] === true && !fileuri.path.includes('/vendor/')) {
+		if (goConfig['inferGopath'] === true && !fileuri?.path.includes('/vendor/')) {
 			goConfig.update('inferGopath', false, vscode.ConfigurationTarget.WorkspaceFolder);
 			vscode.window.showInformationMessage(
 				'The "inferGopath" setting is disabled for this workspace because Go modules are being used.'
@@ -122,7 +122,7 @@
 					if (goConfig.get('useLanguageServer') === false) {
 						goConfig.update('useLanguageServer', true, vscode.ConfigurationTarget.Global);
 					}
-					if (goConfig.inspect('useLanguageServer').workspaceFolderValue === false) {
+					if (goConfig.inspect('useLanguageServer')?.workspaceFolderValue === false) {
 						goConfig.update('useLanguageServer', true, vscode.ConfigurationTarget.WorkspaceFolder);
 					}
 					break;
@@ -149,7 +149,7 @@
 	}
 
 	const moduleCache = getModuleCache();
-	if (cwd.startsWith(moduleCache)) {
+	if (moduleCache && cwd.startsWith(moduleCache)) {
 		let importPath = cwd.substr(moduleCache.length + 1);
 		const matches = /@v\d+(\.\d+)?(\.\d+)?/.exec(importPath);
 		if (matches) {
@@ -165,7 +165,7 @@
 		console.warn(
 			`Failed to run "go list" to find current package as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
-		return;
+		return '';
 	}
 	return new Promise<string>((resolve) => {
 		const childProcess = cp.spawn(goRuntimePath, ['list'], { cwd, env: toolExecutionEnvironment() });
@@ -200,17 +200,21 @@
 		placeHolder: 'example/project'
 	});
 
+	if (!moduleName) {
+		return;
+	}
+
 	const goRuntimePath = getBinPath('go');
 	const execFile = util.promisify(cp.execFile);
 	try {
 		const env = toolExecutionEnvironment();
-		const cwd = getWorkspaceFolderPath();
+		const cwd = getWorkspaceFolderPath() ?? '';
 		outputChannel.appendLine(`Running "${goRuntimePath} mod init ${moduleName}"`);
 		await execFile(goRuntimePath, ['mod', 'init', moduleName], { env, cwd });
 		outputChannel.appendLine('Module successfully initialized. You are ready to Go :)');
 		vscode.commands.executeCommand('vscode.open', vscode.Uri.file(path.join(cwd, 'go.mod')));
 	} catch (e) {
-		outputChannel.appendLine(e);
+		outputChannel.appendLine((e as Error).message);
 		outputChannel.show();
 		vscode.window.showErrorMessage(
 			`Error running "${goRuntimePath} mod init ${moduleName}": See Go output channel for details`
diff --git a/src/goPackages.ts b/src/goPackages.ts
index af221da..e02828a 100644
--- a/src/goPackages.ts
+++ b/src/goPackages.ts
@@ -124,7 +124,7 @@
 			goListPkgs(workDir).then((pkgMap) => {
 				goListPkgsRunning.delete(workDir);
 				goListPkgsSubscriptions.delete(workDir);
-				subs.forEach((cb) => cb(pkgMap));
+				subs?.forEach((cb) => cb(pkgMap));
 			});
 		}
 	});
@@ -265,7 +265,7 @@
 		console.warn(
 			`Failed to run "go list" to find packages as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) PATH(${envPath})`
 		);
-		return;
+		return Promise.resolve(new Map());
 	}
 
 	return new Promise<Map<string, string>>((resolve, reject) => {
diff --git a/src/goPlayground.ts b/src/goPlayground.ts
index 3d0ad08..dc16de4 100644
--- a/src/goPlayground.ts
+++ b/src/goPlayground.ts
@@ -32,7 +32,8 @@
 
 	const selection = editor.selection;
 	const code = selection.isEmpty ? editor.document.getText() : editor.document.getText(selection);
-	goPlay(code, getGoConfig(editor.document.uri).get('playground')).then(
+	const config: vscode.WorkspaceConfiguration | undefined = getGoConfig(editor.document.uri).get('playground');
+	goPlay(code, config).then(
 		(result) => {
 			outputChannel.append(result);
 		},
@@ -44,8 +45,8 @@
 	);
 };
 
-export function goPlay(code: string, goConfig: vscode.WorkspaceConfiguration): Thenable<string> {
-	const cliArgs = Object.keys(goConfig).map((key) => `-${key}=${goConfig[key]}`);
+export function goPlay(code: string, goConfig?: vscode.WorkspaceConfiguration): Thenable<string> {
+	const cliArgs = goConfig ? Object.keys(goConfig).map((key) => `-${key}=${goConfig[key]}`) : [];
 	const binaryLocation = getBinPath(TOOL_CMD_NAME);
 
 	return new Promise<string>((resolve, reject) => {
@@ -64,7 +65,7 @@
 			);
 		});
 		if (p.pid) {
-			p.stdin.end(code);
+			p.stdin?.end(code);
 		}
 	});
 }
diff --git a/src/goStatus.ts b/src/goStatus.ts
index 6925623..56360e5 100644
--- a/src/goStatus.ts
+++ b/src/goStatus.ts
@@ -39,7 +39,7 @@
 export const languageServerIcon = '$(zap)';
 export const languageServerErrorIcon = '$(warning)';
 
-export async function updateGoStatusBar(editor: vscode.TextEditor) {
+export async function updateGoStatusBar(editor: vscode.TextEditor | undefined) {
 	// Only update the module path if we are in a Go file.
 	// This allows the user to open output windows without losing
 	// the go.mod information in the status bar.
@@ -105,11 +105,13 @@
 					break;
 				case "Open 'go.work'":
 				case "Open 'go.mod'":
-					const openPath = vscode.Uri.file(item.description);
-					vscode.workspace.openTextDocument(openPath).then((doc) => {
-						vscode.window.showTextDocument(doc);
-					});
-					break;
+					if (item.description) {
+						const openPath = vscode.Uri.file(item.description);
+						vscode.workspace.openTextDocument(openPath).then((doc) => {
+							vscode.window.showTextDocument(doc);
+						});
+						break;
+					}
 			}
 		}
 	});
diff --git a/src/goSurvey.ts b/src/goSurvey.ts
index eeecd79..b55c2d4 100644
--- a/src/goSurvey.ts
+++ b/src/goSurvey.ts
@@ -80,7 +80,7 @@
 	setTimeout(callback, ms);
 }
 
-export function shouldPromptForSurvey(now: Date, cfg: GoplsSurveyConfig): GoplsSurveyConfig {
+export function shouldPromptForSurvey(now: Date, cfg: GoplsSurveyConfig): GoplsSurveyConfig | undefined {
 	// If the prompt value is not set, assume we haven't prompted the user
 	// and should do so.
 	if (cfg.prompt === undefined) {
@@ -145,7 +145,7 @@
 	return Math.floor(Math.random() * (high - low + 1)) + low;
 }
 
-async function promptForGoplsSurvey(cfg: GoplsSurveyConfig, now: Date): Promise<GoplsSurveyConfig> {
+async function promptForGoplsSurvey(cfg: GoplsSurveyConfig = {}, now: Date): Promise<GoplsSurveyConfig> {
 	let selected = await vscode.window.showInformationMessage(
 		`Looks like you are using the Go extension for VS Code.
 Could you help us improve this extension by filling out a 1-2 minute survey about your experience with it?`,
diff --git a/src/goTest.ts b/src/goTest.ts
index 09875ad..d13d1ef 100644
--- a/src/goTest.ts
+++ b/src/goTest.ts
@@ -22,12 +22,12 @@
 
 // lastTestConfig holds a reference to the last executed TestConfig which allows
 // the last test to be easily re-executed.
-let lastTestConfig: TestConfig;
+let lastTestConfig: TestConfig | undefined;
 
 // lastDebugConfig holds a reference to the last executed DebugConfiguration which allows
 // the last test to be easily re-executed and debugged.
-let lastDebugConfig: vscode.DebugConfiguration;
-let lastDebugWorkspaceFolder: vscode.WorkspaceFolder;
+let lastDebugConfig: vscode.DebugConfiguration | undefined;
+let lastDebugWorkspaceFolder: vscode.WorkspaceFolder | undefined;
 
 export type TestAtCursorCmd = 'debug' | 'test' | 'benchmark';
 
@@ -43,13 +43,13 @@
 	}
 
 	const getFunctions = cmd === 'benchmark' ? getBenchmarkFunctions : getTestFunctions;
-	const testFunctions = await getFunctions(editor.document, null);
+	const testFunctions = (await getFunctions(editor.document)) ?? [];
 	// We use functionName if it was provided as argument
 	// Otherwise find any test function containing the cursor.
 	const testFunctionName =
 		args && args.functionName
 			? args.functionName
-			: testFunctions.filter((func) => func.range.contains(editor.selection.start)).map((el) => el.name)[0];
+			: testFunctions?.filter((func) => func.range.contains(editor.selection.start)).map((el) => el.name)[0];
 	if (!testFunctionName) {
 		throw new NotFoundError('No test function found at cursor.');
 	}
@@ -154,7 +154,7 @@
 
 	await editor.document.save();
 	try {
-		const testFunctions = await getTestFunctions(editor.document, null);
+		const testFunctions = (await getTestFunctions(editor.document)) ?? [];
 		// We use functionName if it was provided as argument
 		// Otherwise find any test function containing the cursor.
 		const currentTestFunctions = testFunctions.filter((func) => func.range.contains(editor.selection.start));
@@ -170,8 +170,8 @@
 		const simpleRunRegex = /t.Run\("([^"]+)",/;
 		const runRegex = /t.Run\(/;
 		let lineText: string;
-		let runMatch: RegExpMatchArray | null;
-		let simpleMatch: RegExpMatchArray | null;
+		let runMatch: RegExpMatchArray | null | undefined;
+		let simpleMatch: RegExpMatchArray | null | undefined;
 		for (let i = editor.selection.start.line; i >= testFunction.range.start.line; i--) {
 			lineText = editor.document.lineAt(i).text;
 			simpleMatch = lineText.match(simpleRunRegex);
@@ -200,7 +200,7 @@
 
 		return await runTestAtCursor(editor, subTestName, testFunctions, goConfig, 'test', args);
 	} catch (err) {
-		vscode.window.showInformationMessage('Unable to run subtest: ' + err.toString());
+		vscode.window.showInformationMessage('Unable to run subtest: ' + (err as any).toString());
 		console.error(err);
 	}
 }
@@ -289,16 +289,16 @@
  * @param goConfig Configuration for the Go extension.
  */
 export function testWorkspace(goConfig: vscode.WorkspaceConfiguration, args: any) {
-	if (!vscode.workspace.workspaceFolders.length) {
+	if (!vscode.workspace.workspaceFolders?.length) {
 		vscode.window.showInformationMessage('No workspace is open to run tests.');
 		return;
 	}
-	let workspaceUri = vscode.workspace.workspaceFolders[0].uri;
+	let workspaceUri: vscode.Uri | undefined = vscode.workspace.workspaceFolders[0].uri;
 	if (
 		vscode.window.activeTextEditor &&
 		vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri)
 	) {
-		workspaceUri = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri).uri;
+		workspaceUri = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri)!.uri;
 	}
 
 	const testConfig: TestConfig = {
@@ -332,11 +332,11 @@
 	const editor = vscode.window.activeTextEditor;
 	if (!editor) {
 		vscode.window.showInformationMessage('No editor is active.');
-		return;
+		return false;
 	}
 	if (!editor.document.fileName.endsWith('_test.go')) {
 		vscode.window.showInformationMessage('No tests found. Current file is not a test file.');
-		return;
+		return false;
 	}
 
 	const getFunctions = isBenchmark ? getBenchmarkFunctions : getTestFunctions;
@@ -345,12 +345,12 @@
 	return editor.document
 		.save()
 		.then(() => {
-			return getFunctions(editor.document, null).then((testFunctions) => {
+			return getFunctions(editor.document).then((testFunctions) => {
 				const testConfig: TestConfig = {
 					goConfig,
 					dir: path.dirname(editor.document.fileName),
 					flags: getTestFlags(goConfig, args),
-					functions: testFunctions.map((sym) => sym.name),
+					functions: testFunctions?.map((sym) => sym.name),
 					isBenchmark,
 					isMod,
 					applyCodeCoverage: goConfig.get<boolean>('coverOnSingleTestFile')
@@ -360,7 +360,7 @@
 				return goTest(testConfig);
 			});
 		})
-		.then(null, (err) => {
+		.then(undefined, (err) => {
 			console.error(err);
 			return Promise.resolve(false);
 		});
diff --git a/src/goTools.ts b/src/goTools.ts
index a4b2007..65d2fdc 100644
--- a/src/goTools.ts
+++ b/src/goTools.ts
@@ -36,15 +36,15 @@
 	// latestVersion and latestVersionTimestamp are hardcoded default values
 	// for the last known version of the given tool. We also hardcode values
 	// for the latest known pre-release of the tool for the Nightly extension.
-	latestVersion?: semver.SemVer;
+	latestVersion?: semver.SemVer | null;
 	latestVersionTimestamp?: moment.Moment;
-	latestPrereleaseVersion?: semver.SemVer;
+	latestPrereleaseVersion?: semver.SemVer | null;
 	latestPrereleaseVersionTimestamp?: moment.Moment;
 
 	// minimumGoVersion and maximumGoVersion set the range for the versions of
 	// Go with which this tool can be used.
-	minimumGoVersion?: semver.SemVer;
-	maximumGoVersion?: semver.SemVer;
+	minimumGoVersion?: semver.SemVer | null;
+	maximumGoVersion?: semver.SemVer | null;
 
 	// close performs any shutdown tasks that a tool must execute before a new
 	// version is installed. It returns a string containing an error message on
@@ -67,7 +67,7 @@
  */
 export function getImportPath(tool: Tool, goVersion: GoVersion): string {
 	// For older versions of Go, install the older version of gocode.
-	if (tool.name === 'gocode' && goVersion.lt('1.10')) {
+	if (tool.name === 'gocode' && goVersion?.lt('1.10')) {
 		return 'github.com/nsf/gocode';
 	}
 	return tool.importPath;
@@ -75,7 +75,7 @@
 
 export function getImportPathWithVersion(
 	tool: Tool,
-	version: semver.SemVer | string | undefined,
+	version: semver.SemVer | string | undefined | null,
 	goVersion: GoVersion
 ): string {
 	const importPath = getImportPath(tool, goVersion);
@@ -129,7 +129,7 @@
 	// TODO(github.com/golang/vscode-go/issues/388): decide what to do when
 	// the go version is no longer supported by gopls while the legacy tools are
 	// no longer working (or we remove the legacy language feature providers completely).
-	const useLanguageServer = goConfig['useLanguageServer'] && goVersion.gt('1.11');
+	const useLanguageServer = goConfig['useLanguageServer'] && goVersion?.gt('1.11');
 
 	const tools: Tool[] = [];
 	function maybeAddTool(name: string) {
@@ -166,7 +166,7 @@
 	}
 
 	// gocode-gomod needed in go 1.11 & higher
-	if (goVersion.gt('1.10')) {
+	if (goVersion?.gt('1.10')) {
 		maybeAddTool('gocode-gomod');
 	}
 
diff --git a/src/goVet.ts b/src/goVet.ts
index 09ec8b7..25436e6 100644
--- a/src/goVet.ts
+++ b/src/goVet.ts
@@ -27,14 +27,14 @@
 		vscode.window.showInformationMessage('No editor is active, cannot find current package to vet');
 		return;
 	}
-	if (editor.document.languageId !== 'go' && !vetWorkspace) {
+	if (editor?.document.languageId !== 'go' && !vetWorkspace) {
 		vscode.window.showInformationMessage(
 			'File in the active editor is not a Go file, cannot find current package to vet'
 		);
 		return;
 	}
 
-	const documentUri = editor ? editor.document.uri : null;
+	const documentUri = editor?.document.uri;
 	const goConfig = getGoConfig(documentUri);
 
 	outputChannel.clear(); // Ensures stale output from vet on save is cleared
@@ -43,7 +43,7 @@
 
 	goVet(documentUri, goConfig, vetWorkspace)
 		.then((warnings) => {
-			handleDiagnosticErrors(editor ? editor.document : null, warnings, vetDiagnosticCollection);
+			handleDiagnosticErrors(editor?.document, warnings, vetDiagnosticCollection);
 			diagnosticsStatusBarItem.hide();
 		})
 		.catch((err) => {
@@ -60,7 +60,7 @@
  * @param vetWorkspace If true vets code in all workspace.
  */
 export async function goVet(
-	fileUri: vscode.Uri,
+	fileUri: vscode.Uri | undefined,
 	goConfig: vscode.WorkspaceConfiguration,
 	vetWorkspace?: boolean
 ): Promise<ICheckResult[]> {
@@ -75,7 +75,7 @@
 	tokenSource = new vscode.CancellationTokenSource();
 
 	const currentWorkspace = getWorkspaceFolderPath(fileUri);
-	const cwd = vetWorkspace && currentWorkspace ? currentWorkspace : path.dirname(fileUri.fsPath);
+	const cwd = vetWorkspace && currentWorkspace ? currentWorkspace : (fileUri && path.dirname(fileUri.fsPath)) ?? '';
 	if (!path.isAbsolute(cwd)) {
 		return Promise.resolve([]);
 	}
@@ -112,7 +112,7 @@
 	outputChannel.appendLine(`Starting "go vet" under the folder ${cwd}`);
 
 	running = true;
-	return runTool(vetArgs, cwd, 'warning', true, null, vetEnv, false, tokenSource.token).then((result) => {
+	return runTool(vetArgs, cwd, 'warning', true, '', vetEnv, false, tokenSource.token).then((result) => {
 		if (closureEpoch === epoch) {
 			running = false;
 		}
diff --git a/src/goVulncheck.ts b/src/goVulncheck.ts
index 097ec47..756e1af 100644
--- a/src/goVulncheck.ts
+++ b/src/goVulncheck.ts
@@ -60,15 +60,20 @@
 			default:
 				return;
 		}
+		if (!dir) {
+			return;
+		}
 
 		let result = '\nNo known vulnerabilities found.';
 		try {
 			const vuln = await vulncheck(dir, pattern);
-			if (vuln.Vuln) {
+			if (vuln?.Vuln) {
 				result = vuln.Vuln.map(renderVuln).join('----------------------\n');
 			}
 		} catch (e) {
-			result = e;
+			if (e instanceof Error) {
+				result = e.message;
+			}
 			vscode.window.showErrorMessage(`error running vulncheck: ${e}`);
 		}
 
@@ -81,7 +86,7 @@
 	private async activeDir() {
 		const folders = vscode.workspace.workspaceFolders;
 		if (!folders || folders.length === 0) return;
-		let dir = '';
+		let dir: string | undefined = '';
 		if (folders.length === 1) {
 			dir = folders[0].uri.path;
 		} else {