Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as tas from 'vscode-tas-client';
import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter';

interface ExperimentTypes {
// None for now.
suggestNativePreview: boolean;
}

export class ExperimentationService {
Expand All @@ -24,8 +24,8 @@ export class ExperimentationService {
public async getTreatmentVariable<K extends keyof ExperimentTypes>(name: K, defaultValue: ExperimentTypes[K]): Promise<ExperimentTypes[K]> {
const experimentationService = await this._experimentationServicePromise;
try {
const treatmentVariable = experimentationService.getTreatmentVariableAsync('vscode', name, /*checkCache*/ true) as Promise<ExperimentTypes[K]>;
return treatmentVariable;
const treatmentVariable = await experimentationService.getTreatmentVariableAsync('vscode', name, /*checkCache*/ true) as ExperimentTypes[K];
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await experimentationService.getTreatmentVariableAsync(...) as ExperimentTypes[K] relies on await/type-assertion precedence and is easy to misread as asserting the Promise rather than the resolved value. Consider restructuring to await first and then apply the type assertion (or otherwise avoid the combined await ... as ... form) to keep the typing accurate and the intent clear.

Suggested change
const treatmentVariable = await experimentationService.getTreatmentVariableAsync('vscode', name, /*checkCache*/ true) as ExperimentTypes[K];
const rawTreatmentVariable = await experimentationService.getTreatmentVariableAsync('vscode', name, /*checkCache*/ true);
const treatmentVariable = rawTreatmentVariable as ExperimentTypes[K];

Copilot uses AI. Check for mistakes.
return treatmentVariable ?? defaultValue;
} catch {
return defaultValue;
}
Expand All @@ -36,7 +36,8 @@ export async function createTasExperimentationService(
reporter: IExperimentationTelemetryReporter,
id: string,
version: string,
globalState: vscode.Memento): Promise<tas.IExperimentationService> {
globalState: vscode.Memento
): Promise<tas.IExperimentationService> {
let targetPopulation: tas.TargetPopulation;
switch (vscode.env.uriScheme) {
case 'vscode':
Expand Down
6 changes: 3 additions & 3 deletions extensions/typescript-language-features/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { PluginManager } from './tsServer/plugins';
import { ElectronServiceProcessFactory } from './tsServer/serverProcess.electron';
import { DiskTypeScriptVersionProvider } from './tsServer/versionProvider.electron';
import { ActiveJsTsEditorTracker } from './ui/activeJsTsEditorTracker';
import { suggestNativePreview } from './ui/suggestNativePreview';
import { onCaseInsensitiveFileSystem } from './utils/fs.electron';
import { Lazy } from './utils/lazy';
import { getPackageInfo } from './utils/packageInfo';
Expand Down Expand Up @@ -48,9 +49,8 @@ export function activate(
experimentTelemetryReporter = new ExperimentationTelemetryReporter(vscTelemetryReporter);
context.subscriptions.push(experimentTelemetryReporter);

// Currently we have no experiments, but creating the service adds the appropriate
// shared properties to the ExperimentationTelemetryReporter we just created.
new ExperimentationService(experimentTelemetryReporter, id, version, context.globalState);
const experimentationService = new ExperimentationService(experimentTelemetryReporter, id, version, context.globalState);
suggestNativePreview(context, experimentationService);
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to suggestNativePreview returns a Promise but is neither awaited nor explicitly handled. This can lead to unhandled promise rejections during activation if the prompt/command throws. Consider either awaiting it (if activation should wait) or prefixing with void and adding internal error handling inside suggestNativePreview (e.g. a top-level try/catch) to ensure failures don’t surface as unhandled rejections.

Suggested change
suggestNativePreview(context, experimentationService);
void suggestNativePreview(context, experimentationService).catch(() => {
// Ignore errors from this non-blocking suggestion flow during activation.
});

Copilot uses AI. Check for mistakes.
}

// Register features that work in both TSGO and non-TSGO modes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { tsNativeExtensionId } from '../commands/useTsgo';
import { ExperimentationService } from '../experimentationService';

const suggestNativePreviewStorageKey = 'typescript.suggestNativePreview.dismissed';

export async function suggestNativePreview(
context: vscode.ExtensionContext,
experimentationService: ExperimentationService,
): Promise<void> {
if (context.globalState.get<boolean>(suggestNativePreviewStorageKey)) {
return;
}

// Only show when the window is active
if (!vscode.window.state.active) {
return;
}

// Only show when the nightly extension is installed
if (!vscode.extensions.getExtension('ms-vscode.vscode-typescript-next')) {
return;
}

// Don't show if the native preview extension is already installed
if (vscode.extensions.getExtension(tsNativeExtensionId)) {
// Also don't prompt in the future
await context.globalState.update(suggestNativePreviewStorageKey, true);
return;
}

const inExperiment = await experimentationService.getTreatmentVariable('suggestNativePreview', false);
if (!inExperiment) {
return;
}

const install: vscode.MessageItem = { title: vscode.l10n.t("Install") };
const learnMore: vscode.MessageItem = { title: vscode.l10n.t("Learn More") };
const dismiss: vscode.MessageItem = { title: vscode.l10n.t("Don't Show Again") };

const selection = await vscode.window.showInformationMessage(
vscode.l10n.t("Try TypeScript 7 Native Preview for significantly faster type checking and language features."),
{},
install,
learnMore,
dismiss,
);
// Don't show again
await context.globalState.update(suggestNativePreviewStorageKey, true);

if (selection === install) {
await vscode.commands.executeCommand('workbench.extensions.installExtension', tsNativeExtensionId);
} else if (selection === learnMore) {
await vscode.env.openExternal(vscode.Uri.parse('https://aka.ms/vscode-try-ts-7-learn-more'));
}
}
Loading