Refactor: Eliminate no-unsafe-return suppressions via strict type validation (#20668)

Signed-off-by: M-DEV-1 <mahadevankizhakkedathu@gmail.com>
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
This commit is contained in:
mahadevan
2026-05-13 05:15:58 +05:30
committed by GitHub
parent 8f03aa320e
commit 31d5947d37
23 changed files with 184 additions and 115 deletions
+5 -2
View File
@@ -60,8 +60,11 @@ export class AcpFileSystemService implements FileSystemService {
sessionId: this.sessionId,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.content;
const content: unknown = response.content;
if (typeof content !== 'string') {
throw new Error('content must be a string'); // replace with other response type formats when modified in the future
}
return content;
} catch (err: unknown) {
this.normalizeFileSystemError(err);
}
@@ -27,7 +27,7 @@ export interface ConfigLogger {
export type RequestSettingCallback = (
setting: ExtensionSetting,
) => Promise<string>;
) => Promise<string | undefined>;
export type RequestConfirmationCallback = (message: string) => Promise<boolean>;
const defaultLogger: ConfigLogger = {
@@ -47,8 +47,7 @@ const defaultRequestConfirmation: RequestConfirmationCallback = async (
message,
initial: false,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.confirm;
return typeof response.confirm === 'boolean' ? response.confirm : false;
};
export async function getExtensionManager() {
+5 -3
View File
@@ -88,7 +88,9 @@ interface ExtensionManagerParams {
enabledExtensionOverrides?: string[];
settings: MergedSettings;
requestConsent: (consent: string) => Promise<boolean>;
requestSetting: ((setting: ExtensionSetting) => Promise<string>) | null;
requestSetting:
| ((setting: ExtensionSetting) => Promise<string | undefined>)
| null;
workspaceDir: string;
eventEmitter?: EventEmitter<ExtensionEvents>;
clientVersion?: string;
@@ -106,7 +108,7 @@ export class ExtensionManager extends ExtensionLoader {
private settings: MergedSettings;
private requestConsent: (consent: string) => Promise<boolean>;
private requestSetting:
| ((setting: ExtensionSetting) => Promise<string>)
| ((setting: ExtensionSetting) => Promise<string | undefined>)
| undefined;
private telemetryConfig: Config;
private workspaceDir: string;
@@ -161,7 +163,7 @@ export class ExtensionManager extends ExtensionLoader {
}
setRequestSetting(
requestSetting?: (setting: ExtensionSetting) => Promise<string>,
requestSetting?: (setting: ExtensionSetting) => Promise<string | undefined>,
): void {
this.requestSetting = requestSetting;
}
@@ -94,9 +94,8 @@ export class ExtensionRegistryClient {
fuzzy: true,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzf.find(query);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return results.map((r: { item: RegistryExtension }) => r.item);
const results: Array<{ item: RegistryExtension }> = await fzf.find(query);
return results.map((r) => r.item);
}
async getExtension(id: string): Promise<RegistryExtension | undefined> {
@@ -8,6 +8,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { coreEvents, type GeminiCLIExtension } from '@google/gemini-cli-core';
import { ExtensionStorage } from './storage.js';
import { z } from 'zod';
export interface ExtensionEnablementConfig {
overrides: string[];
@@ -179,8 +180,12 @@ export class ExtensionEnablementManager {
readConfig(): AllExtensionsEnablementConfig {
try {
const content = fs.readFileSync(this.configFilePath, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(content);
const parsed: unknown = JSON.parse(content);
const schema = z.record(
z.string(),
z.object({ overrides: z.array(z.string()) }),
);
return schema.parse(parsed);
} catch (error) {
if (
error instanceof Error &&
@@ -62,7 +62,7 @@ export const getEnvFilePath = (
export async function maybePromptForSettings(
extensionConfig: ExtensionConfig,
extensionId: string,
requestSetting: (setting: ExtensionSetting) => Promise<string>,
requestSetting: (setting: ExtensionSetting) => Promise<string | undefined>,
previousExtensionConfig?: ExtensionConfig,
previousSettings?: Record<string, string>,
): Promise<void> {
@@ -106,7 +106,9 @@ export async function maybePromptForSettings(
settingsChanges.promptForEnv,
)) {
const answer = await requestSetting(setting);
allSettings[setting.envVar] = answer;
if (answer !== undefined) {
allSettings[setting.envVar] = answer;
}
}
const nonSensitiveSettings: Record<string, string> = {};
@@ -159,14 +161,13 @@ function formatEnvContent(settings: Record<string, string>): string {
export async function promptForSetting(
setting: ExtensionSetting,
): Promise<string> {
): Promise<string | undefined> {
const response = await prompts({
type: setting.sensitive ? 'password' : 'text',
name: 'value',
message: `${setting.name}\n${setting.description}`,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.value;
return typeof response.value === 'string' ? response.value : undefined;
}
export async function getScopedEnvContents(
@@ -230,7 +231,7 @@ export async function updateSetting(
extensionConfig: ExtensionConfig,
extensionId: string,
settingKey: string,
requestSetting: (setting: ExtensionSetting) => Promise<string>,
requestSetting: (setting: ExtensionSetting) => Promise<string | undefined>,
scope: ExtensionSettingScope,
workspaceDir: string,
): Promise<void> {
@@ -250,6 +251,10 @@ export async function updateSetting(
}
const newValue = await requestSetting(settingToUpdate);
if (newValue === undefined) {
return;
}
const keychain = new KeychainTokenStorage(
getKeychainStorageName(extensionName, extensionId, scope, workspaceDir),
);
@@ -67,8 +67,7 @@ export function recursivelyHydrateStrings<T>(
}
if (Array.isArray(obj)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return obj.map((item) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return (obj as unknown[]).map((item) =>
recursivelyHydrateStrings(item, values),
) as unknown as T;
}
@@ -112,5 +112,11 @@ export const createMockCommandContext = (
return output;
};
return merge(defaultMocks, overrides);
const merged: unknown = merge(defaultMocks, overrides);
const isCommandContext = (val: unknown): val is CommandContext =>
typeof val === 'object' && val !== null;
if (isCommandContext(merged)) {
return merged;
}
throw new Error('Unreachable');
};
+13 -11
View File
@@ -170,13 +170,13 @@ async function searchResourceCandidates(
selector: (candidate: ResourceSuggestionCandidate) => candidate.searchKey,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW * 3,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return results.map(
(result: { item: ResourceSuggestionCandidate }) => result.item.suggestion,
const results: Array<{ item: ResourceSuggestionCandidate }> = await fzf.find(
normalizedPattern,
{
limit: MAX_SUGGESTIONS_TO_SHOW * 3,
},
);
return results.map((result) => result.item.suggestion);
}
async function searchAgentCandidates(
@@ -194,11 +194,13 @@ async function searchAgentCandidates(
selector: (s: Suggestion) => s.label,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return results.map((r: { item: Suggestion }) => r.item);
const results: Array<{ item: Suggestion }> = await fzf.find(
normalizedPattern,
{
limit: MAX_SUGGESTIONS_TO_SHOW,
},
);
return results.map((r) => r.item);
}
export function useAtCompletion(props: UseAtCompletionProps): void {
+5 -7
View File
@@ -174,7 +174,10 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
}
// --- Pre-wrap and Optimize Widths ---
const actualColumnWidths = new Array(numColumns).fill(0);
const actualColumnWidths: number[] = [];
for (let i = 0; i < numColumns; i++) {
actualColumnWidths.push(0);
}
const wrapAndProcessRow = (row: StyledLine[]) => {
const rowResult: ProcessedLine[][] = [];
@@ -208,11 +211,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
const wrappedRows = styledRows.map((row) => wrapAndProcessRow(row));
// Use the TIGHTEST widths that fit the wrapped content + padding
const adjustedWidths = actualColumnWidths.map(
(w) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
w + COLUMN_PADDING,
);
const adjustedWidths = actualColumnWidths.map((w) => w + COLUMN_PADDING);
return { wrappedHeaders, wrappedRows, adjustedWidths };
}, [styledHeaders, styledRows, terminalWidth]);
@@ -263,7 +262,6 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
isHeader = false,
): React.ReactNode => {
const renderedCells = cells.map((cell, index) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const width = adjustedWidths[index] || 0;
return renderCell(cell, width, isHeader);
});
+9 -7
View File
@@ -111,18 +111,20 @@ function resolveEnvVarsInObjectInternal<T>(
// Check for circular reference
if (visited.has(obj)) {
// Return a shallow copy to break the cycle
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return [...obj] as unknown as T;
const copy: unknown = [...obj];
const isTArray = (val: unknown): val is T => Array.isArray(val);
if (isTArray(copy)) return copy;
throw new Error('Unreachable');
}
visited.add(obj);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const result = obj.map((item) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const mapped: unknown = obj.map((item: unknown) =>
resolveEnvVarsInObjectInternal(item, visited, customEnv),
) as unknown as T;
);
visited.delete(obj);
return result;
const isTArray = (val: unknown): val is T => Array.isArray(val);
if (isTArray(mapped)) return mapped;
throw new Error('Unreachable');
}
if (typeof obj === 'object') {
+1 -2
View File
@@ -83,8 +83,7 @@ export const getLatestGitHubRelease = async (
if (!releaseTag) {
throw new Error(`Response did not include tag_name field`);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return releaseTag;
return typeof releaseTag === 'string' ? releaseTag : '';
} catch (error) {
debugLogger.debug(
`Failed to determine latest run-gemini-cli release:`,
+1 -3
View File
@@ -29,8 +29,7 @@ export function tryParseJSON(input: string): object | null {
if (!checkInput(input)) return null;
const trimmed = input.trim();
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsed = JSON.parse(trimmed);
const parsed: unknown = JSON.parse(trimmed);
if (parsed === null || typeof parsed !== 'object') {
return null;
}
@@ -40,7 +39,6 @@ export function tryParseJSON(input: string): object | null {
if (!Array.isArray(parsed) && Object.keys(parsed).length === 0) return null;
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return parsed;
} catch {
return null;