mirror of
https://github.com/google-gemini/gemini-cli
synced 2026-04-21 13:37:17 +00:00
Disallow underspecified types (#21485)
This commit is contained in:
parent
245b68e9f1
commit
dac3735626
18 changed files with 51 additions and 7 deletions
|
|
@ -132,7 +132,16 @@ export default tseslint.config(
|
|||
'no-cond-assign': 'error',
|
||||
'no-debugger': 'error',
|
||||
'no-duplicate-case': 'error',
|
||||
'no-restricted-syntax': ['error', ...commonRestrictedSyntaxRules],
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
...commonRestrictedSyntaxRules,
|
||||
{
|
||||
selector:
|
||||
'UnaryExpression[operator="typeof"] > MemberExpression[computed=true][property.type="Literal"]',
|
||||
message:
|
||||
'Do not use typeof to check object properties. Define a TypeScript interface and a type guard function instead.',
|
||||
},
|
||||
],
|
||||
'no-unsafe-finally': 'error',
|
||||
'no-unused-expressions': 'off', // Disable base rule
|
||||
'@typescript-eslint/no-unused-expressions': [
|
||||
|
|
@ -263,6 +272,7 @@ export default tseslint.config(
|
|||
...vitest.configs.recommended.rules,
|
||||
'vitest/expect-expect': 'off',
|
||||
'vitest/no-commented-out-tests': 'off',
|
||||
'no-restricted-syntax': ['error', ...commonRestrictedSyntaxRules],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -832,7 +832,9 @@ export class Task {
|
|||
if (
|
||||
part.kind !== 'data' ||
|
||||
!part.data ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof part.data['callId'] !== 'string' ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof part.data['outcome'] !== 'string'
|
||||
) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ function migrateClaudeHook(claudeHook: unknown): unknown {
|
|||
migrated['command'] = hook['command'];
|
||||
|
||||
// Replace CLAUDE_PROJECT_DIR with GEMINI_PROJECT_DIR in command
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
if (typeof migrated['command'] === 'string') {
|
||||
migrated['command'] = migrated['command'].replace(
|
||||
/\$CLAUDE_PROJECT_DIR/g,
|
||||
|
|
@ -93,6 +94,7 @@ function migrateClaudeHook(claudeHook: unknown): unknown {
|
|||
}
|
||||
|
||||
// Map timeout field (Claude uses seconds, Gemini uses seconds)
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
if ('timeout' in hook && typeof hook['timeout'] === 'number') {
|
||||
migrated['timeout'] = hook['timeout'];
|
||||
}
|
||||
|
|
@ -140,6 +142,7 @@ function migrateClaudeHooks(claudeConfig: unknown): Record<string, unknown> {
|
|||
// Transform matcher
|
||||
if (
|
||||
'matcher' in definition &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof definition['matcher'] === 'string'
|
||||
) {
|
||||
migratedDef['matcher'] = transformMatcher(definition['matcher']);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export function mockCoreDebugLogger<T extends Record<string, unknown>>(
|
|||
return {
|
||||
...actual,
|
||||
coreEvents: {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
...(typeof actual['coreEvents'] === 'object' &&
|
||||
actual['coreEvents'] !== null
|
||||
? actual['coreEvents']
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ function isInkRenderMetrics(
|
|||
typeof m === 'object' &&
|
||||
m !== null &&
|
||||
'output' in m &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof m['output'] === 'string'
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -502,7 +502,9 @@ export const useSlashCommandProcessor = (
|
|||
const props = result.props as Record<string, unknown>;
|
||||
if (
|
||||
!props ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof props['name'] !== 'string' ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof props['displayName'] !== 'string' ||
|
||||
!props['definition']
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -494,9 +494,10 @@ export class ActivityLogger extends EventEmitter {
|
|||
|
||||
req.write = function (chunk: string | Uint8Array, ...etc: unknown[]) {
|
||||
if (chunk) {
|
||||
const arg0 = etc[0];
|
||||
const encoding =
|
||||
typeof etc[0] === 'string' && Buffer.isEncoding(etc[0])
|
||||
? etc[0]
|
||||
typeof arg0 === 'string' && Buffer.isEncoding(arg0)
|
||||
? arg0
|
||||
: undefined;
|
||||
requestChunks.push(
|
||||
Buffer.isBuffer(chunk)
|
||||
|
|
@ -519,9 +520,10 @@ export class ActivityLogger extends EventEmitter {
|
|||
) {
|
||||
const chunk = typeof chunkOrCb === 'function' ? undefined : chunkOrCb;
|
||||
if (chunk) {
|
||||
const arg0 = etc[0];
|
||||
const encoding =
|
||||
typeof etc[0] === 'string' && Buffer.isEncoding(etc[0])
|
||||
? etc[0]
|
||||
typeof arg0 === 'string' && Buffer.isEncoding(arg0)
|
||||
? arg0
|
||||
: undefined;
|
||||
requestChunks.push(
|
||||
Buffer.isBuffer(chunk)
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
|||
|
||||
if (
|
||||
activity.type === 'THOUGHT_CHUNK' &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof activity.data['text'] === 'string'
|
||||
) {
|
||||
updateOutput(`🌐💭 ${activity.data['text']}`);
|
||||
|
|
|
|||
|
|
@ -356,6 +356,7 @@ class TypeTextDeclarativeTool extends DeclarativeTool<
|
|||
params: Record<string, unknown>,
|
||||
): ToolInvocation<Record<string, unknown>, ToolResult> {
|
||||
const submitKey =
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof params['submitKey'] === 'string' && params['submitKey']
|
||||
? params['submitKey']
|
||||
: undefined;
|
||||
|
|
|
|||
|
|
@ -355,6 +355,7 @@ export class HookAggregator {
|
|||
// Extract additionalContext from various hook types
|
||||
if (
|
||||
'additionalContext' in specific &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof specific['additionalContext'] === 'string'
|
||||
) {
|
||||
contexts.push(specific['additionalContext']);
|
||||
|
|
|
|||
|
|
@ -132,7 +132,11 @@ export class FolderTrustDiscoveryService {
|
|||
for (const event of Object.values(hooksConfig)) {
|
||||
if (!Array.isArray(event)) continue;
|
||||
for (const hook of event) {
|
||||
if (this.isRecord(hook) && typeof hook['command'] === 'string') {
|
||||
if (
|
||||
this.isRecord(hook) &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof hook['command'] === 'string'
|
||||
) {
|
||||
hooks.add(hook['command']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,11 +156,13 @@ async function truncateHistoryToBudget(
|
|||
} else if (responseObj && typeof responseObj === 'object') {
|
||||
if (
|
||||
'output' in responseObj &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof responseObj['output'] === 'string'
|
||||
) {
|
||||
contentStr = responseObj['output'];
|
||||
} else if (
|
||||
'content' in responseObj &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof responseObj['content'] === 'string'
|
||||
) {
|
||||
contentStr = responseObj['content'];
|
||||
|
|
|
|||
|
|
@ -579,10 +579,12 @@ export class LoopDetectionService {
|
|||
}
|
||||
|
||||
const flashConfidence =
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof flashResult['unproductive_state_confidence'] === 'number'
|
||||
? flashResult['unproductive_state_confidence']
|
||||
: 0;
|
||||
const flashAnalysis =
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof flashResult['unproductive_state_analysis'] === 'string'
|
||||
? flashResult['unproductive_state_analysis']
|
||||
: '';
|
||||
|
|
@ -628,11 +630,13 @@ export class LoopDetectionService {
|
|||
|
||||
const mainModelConfidence =
|
||||
mainModelResult &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof mainModelResult['unproductive_state_confidence'] === 'number'
|
||||
? mainModelResult['unproductive_state_confidence']
|
||||
: 0;
|
||||
const mainModelAnalysis =
|
||||
mainModelResult &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof mainModelResult['unproductive_state_analysis'] === 'string'
|
||||
? mainModelResult['unproductive_state_analysis']
|
||||
: undefined;
|
||||
|
|
@ -681,6 +685,7 @@ export class LoopDetectionService {
|
|||
|
||||
if (
|
||||
result &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof result['unproductive_state_confidence'] === 'number'
|
||||
) {
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ function getStringReferences(parts: AnyPart[]): StringReference[] {
|
|||
});
|
||||
}
|
||||
} else if (part instanceof GenericPart) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
if (part.type === 'executableCode' && typeof part['code'] === 'string') {
|
||||
refs.push({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
|
|
@ -73,6 +74,7 @@ function getStringReferences(parts: AnyPart[]): StringReference[] {
|
|||
});
|
||||
} else if (
|
||||
part.type === 'codeExecutionResult' &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof part['output'] === 'string'
|
||||
) {
|
||||
refs.push({
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ export function isMcpToolAnnotation(
|
|||
return (
|
||||
typeof annotation === 'object' &&
|
||||
annotation !== null &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, no-restricted-syntax
|
||||
typeof (annotation as Record<string, unknown>)['_serverName'] === 'string'
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ Return ONLY the corrected string in the specified JSON format with the key 'corr
|
|||
|
||||
if (
|
||||
result &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof result['corrected_string_escaping'] === 'string' &&
|
||||
result['corrected_string_escaping'].length > 0
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ export function parseGoogleApiError(error: unknown): GoogleApiError | null {
|
|||
}
|
||||
// Basic structural check before casting.
|
||||
// Since the proto definitions are loose, we primarily rely on @type presence.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
if (typeof detailObj['@type'] === 'string') {
|
||||
// We can just cast it; the consumer will have to switch on @type
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
|
|
|
|||
|
|
@ -361,19 +361,24 @@ async function parseTokenEndpointResponse(
|
|||
data &&
|
||||
typeof data === 'object' &&
|
||||
'access_token' in data &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof (data as Record<string, unknown>)['access_token'] === 'string'
|
||||
) {
|
||||
const obj = data as Record<string, unknown>;
|
||||
const result: OAuthTokenResponse = {
|
||||
access_token: String(obj['access_token']),
|
||||
token_type:
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof obj['token_type'] === 'string' ? obj['token_type'] : 'Bearer',
|
||||
expires_in:
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof obj['expires_in'] === 'number' ? obj['expires_in'] : undefined,
|
||||
refresh_token:
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof obj['refresh_token'] === 'string'
|
||||
? obj['refresh_token']
|
||||
: undefined,
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
scope: typeof obj['scope'] === 'string' ? obj['scope'] : undefined,
|
||||
};
|
||||
return result;
|
||||
|
|
|
|||
Loading…
Reference in a new issue