Skip to content

Commit c13755b

Browse files
flash1293kibanamachinecauemarcondes
authored
[Onboarding] Replace any with unknown for stricter type safety (#257433)
## Summary Adds ESLint rule `@typescript-eslint/no-explicit-any: 'error'` to all packages and plugins owned by the onboarding team, and fixes all existing violations by replacing `any` with `unknown` and adding appropriate type guards. **Key changes:** - Expanded ESLint "Onboarding team overrides" (renamed from "Streams overrides") to cover all team-owned packages including kbn-synthtrace, kbn-streams-*, kbn-streamlang-*, kbn-grok-heuristics, dataset_quality, observability_onboarding, etc. - Replaced `any` with `unknown` throughout, adding type narrowing where needed (typeof checks, `in` operator, explicit casts) - Added helper functions for safer option extraction in synthtrace scenarios (`getNumberOpt`, `getBooleanOpt`, `getStringOpt`) - Used `DependencyList` type from React instead of `any[]` for hook dependencies - Improved F-bounded polymorphism in abstract classes (AbstractSpan) - Used `NodeJS.ErrnoException` for error handling with error codes This is a compile-time only change with no runtime behavior changes. ## Test plan - [ ] CI passes (type checking, linting, unit tests) - [ ] Verify affected packages compile without errors - [ ] No runtime behavior changes expected - purely type safety improvements Clean version of #256881 Made with [Cursor](https://cursor.com) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit # Release Notes * **Refactor** * Improved type safety across the codebase by replacing loose `any` types with `unknown` and explicit type constraints * Enhanced error handling with stricter type guards and null-safety checks * Strengthened schema validation and data model typing for better runtime safety * Updated ESLint configuration to enforce stricter type rules across additional packages <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
1 parent ebf5c03 commit c13755b

155 files changed

Lines changed: 1536 additions & 908 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1885,12 +1885,36 @@ module.exports = {
18851885
},
18861886

18871887
/**
1888-
* Streams overrides
1888+
* Onboarding team overrides
18891889
*/
18901890
{
18911891
files: [
18921892
'x-pack/platform/plugins/shared/streams/**/*.{ts,tsx}',
18931893
'x-pack/platform/plugins/shared/streams_app/**/*.{ts,tsx}',
1894+
'x-pack/solutions/observability/plugins/observability_onboarding/**/*.{ts,tsx}',
1895+
'x-pack/platform/plugins/shared/fields_metadata/**/*.{ts,tsx}',
1896+
'x-pack/platform/plugins/shared/dataset_quality/**/*.{ts,tsx}',
1897+
'x-pack/platform/plugins/shared/data_quality/**/*.{ts,tsx}',
1898+
'x-pack/platform/packages/shared/kbn-streams-schema/**/*.{ts,tsx}',
1899+
'x-pack/platform/packages/shared/kbn-streams-ai/**/*.{ts,tsx}',
1900+
'x-pack/platform/packages/shared/kbn-streamlang/**/*.{ts,tsx}',
1901+
'x-pack/platform/packages/shared/kbn-streamlang-yaml-editor/**/*.{ts,tsx}',
1902+
'x-pack/platform/packages/shared/kbn-streamlang-tests/**/*.{ts,tsx}',
1903+
'x-pack/platform/packages/shared/kbn-sample-parser/**/*.{ts,tsx}',
1904+
'x-pack/platform/packages/shared/kbn-grok-heuristics/**/*.{ts,tsx}',
1905+
'x-pack/platform/packages/shared/kbn-evals-suite-streams/**/*.{ts,tsx}',
1906+
'x-pack/platform/packages/shared/kbn-dissect-heuristics/**/*.{ts,tsx}',
1907+
'x-pack/platform/packages/shared/kbn-content-packs-schema/**/*.{ts,tsx}',
1908+
'x-pack/platform/packages/private/kbn-data-quality/**/*.{ts,tsx}',
1909+
'src/platform/packages/shared/kbn-xstate-utils/**/*.{ts,tsx}',
1910+
'src/platform/packages/shared/kbn-use-tracked-promise/**/*.{ts,tsx}',
1911+
'src/platform/packages/shared/kbn-timerange/**/*.{ts,tsx}',
1912+
'src/platform/packages/shared/kbn-synthtrace/**/*.{ts,tsx}',
1913+
'src/platform/packages/shared/kbn-synthtrace-client/**/*.{ts,tsx}',
1914+
'src/platform/packages/shared/kbn-react-hooks/**/*.{ts,tsx}',
1915+
'src/platform/packages/shared/kbn-otel-semantic-conventions/**/*.{ts,tsx}',
1916+
'src/platform/packages/shared/kbn-otel-demo/**/*.{ts,tsx}',
1917+
'src/platform/packages/shared/kbn-grok-ui/**/*.{ts,tsx}',
18941918
],
18951919
rules: {
18961920
'@typescript-eslint/no-explicit-any': 'error',

src/platform/packages/shared/kbn-otel-demo/src/read_kibana_config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const readKibanaConfig = (log: ToolingLog, configPath?: string): KibanaCo
5151
if (fs.existsSync(configPathToUse)) {
5252
const loaded = (yaml.load(fs.readFileSync(configPathToUse, 'utf8')) || {}) as Record<
5353
string,
54-
any
54+
unknown
5555
>;
5656
const config = unflattenObject(loaded);
5757
esConfigValues = config.elasticsearch || {};

src/platform/packages/shared/kbn-otel-demo/src/util/create_data_view.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,8 @@ export async function createDataView({
8484
}
8585

8686
throw new Error(`Failed to create data view: ${response.status} ${responseText}`);
87-
} catch (error: any) {
88-
// Handle connection errors (Kibana not running)
89-
if (error.code === 'ECONNREFUSED') {
87+
} catch (error: unknown) {
88+
if ((error as NodeJS.ErrnoException).code === 'ECONNREFUSED') {
9089
log.warning(
9190
`Could not connect to Kibana at ${kibanaUrl}. Make sure Kibana is running before starting the OTel Demo.`
9291
);

src/platform/packages/shared/kbn-otel-demo/src/util/enable_streams.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,8 @@ export async function enableStreams({
6767
}
6868

6969
throw new Error(`Failed to enable streams: ${response.status} ${responseText}`);
70-
} catch (error: any) {
71-
// Handle connection errors (Kibana not running)
72-
if (error.code === 'ECONNREFUSED') {
70+
} catch (error: unknown) {
71+
if ((error as NodeJS.ErrnoException).code === 'ECONNREFUSED') {
7372
log.warning(
7473
`Could not connect to Kibana at ${kibanaUrl}. Make sure Kibana is running before starting the OTel Demo.`
7574
);

src/platform/packages/shared/kbn-otel-demo/src/util/resolve_kibana_url.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,16 @@ export async function resolveKibanaUrl(kibanaHostname: string, log?: ToolingLog)
6363
// No base path detected or request succeeded without redirect
6464
log?.debug(`No dev mode base path detected, using: ${kibanaHostname}`);
6565
return kibanaHostname;
66-
} catch (error: any) {
67-
// If we can't connect, just return the original URL
68-
// The actual API call will handle the connection error
69-
log?.debug(
70-
`Could not detect base path (${error.code || error.message}), using: ${kibanaHostname}`
71-
);
66+
} catch (error: unknown) {
67+
const errorObj = typeof error === 'object' && error !== null ? error : undefined;
68+
const code =
69+
errorObj &&
70+
'code' in errorObj &&
71+
typeof (errorObj as Record<string, unknown>).code === 'string'
72+
? ((errorObj as Record<string, unknown>).code as string)
73+
: undefined;
74+
const message = error instanceof Error ? error.message : String(error);
75+
log?.debug(`Could not detect base path (${code ?? message}), using: ${kibanaHostname}`);
7276
return kibanaHostname;
7377
}
7478
}

src/platform/packages/shared/kbn-otel-semantic-conventions/src/lib/generate_semconv.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ function mapOtelTypeToEsType(otelType?: unknown): string {
7777

7878
// Handle complex type objects (like enum definitions with members)
7979
if (typeof otelType === 'object' && otelType !== null) {
80-
const typeObj = otelType as any;
80+
const typeObj = otelType as Record<string, unknown>;
8181

8282
// Check if it's an enum type with members
8383
if (typeObj.members && Array.isArray(typeObj.members)) {

src/platform/packages/shared/kbn-react-hooks/src/use_abortable_async/use_abortable_async.test.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
*/
99

1010
import React, { useEffect, useRef } from 'react';
11+
import type { DependencyList } from 'react';
1112
import { render, act, waitFor } from '@testing-library/react';
12-
import { useAbortableAsync } from './use_abortable_async';
13+
import { useAbortableAsync, type AbortableAsyncState } from './use_abortable_async';
1314

1415
// Helper component to expose hook state.
1516
function TestComponent<T>({
@@ -19,7 +20,7 @@ function TestComponent<T>({
1920
}: {
2021
hookFn: ({ signal }: { signal: AbortSignal }) => T | Promise<T>;
2122
onState: (state: ReturnType<typeof useAbortableAsync>) => void;
22-
deps?: any[];
23+
deps?: DependencyList;
2324
}) {
2425
const state = useAbortableAsync(hookFn, deps);
2526
useEffect(() => {
@@ -31,7 +32,7 @@ function TestComponent<T>({
3132

3233
describe('useAbortableAsync', () => {
3334
it('should handle synchronous function', async () => {
34-
let hookState: any;
35+
let hookState!: AbortableAsyncState<unknown>;
3536
render(
3637
<TestComponent
3738
hookFn={() => 42}
@@ -47,7 +48,7 @@ describe('useAbortableAsync', () => {
4748
});
4849

4950
it('should handle asynchronous success', async () => {
50-
let hookState: any;
51+
let hookState!: AbortableAsyncState<unknown>;
5152
const asyncFn = ({ signal }: { signal: AbortSignal }) =>
5253
new Promise<string>((resolve) => {
5354
const id = setTimeout(() => resolve('async result'), 10);
@@ -73,7 +74,7 @@ describe('useAbortableAsync', () => {
7374
});
7475

7576
it('should handle asynchronous error', async () => {
76-
let hookState: any;
77+
let hookState!: AbortableAsyncState<unknown>;
7778
const errorFn = ({ signal }: { signal: AbortSignal }) =>
7879
new Promise<string>((_resolve, reject) => {
7980
const id = setTimeout(() => reject(new Error('fail')), 10);
@@ -97,7 +98,7 @@ describe('useAbortableAsync', () => {
9798
});
9899

99100
it('should refresh and re-run the async function', async () => {
100-
let hookState: any;
101+
let hookState!: AbortableAsyncState<unknown>;
101102
let counter = 0;
102103
const asyncFn = ({ signal }: { signal: AbortSignal }) =>
103104
new Promise<number>((resolve) => {
@@ -136,7 +137,7 @@ describe('useAbortableAsync', () => {
136137
});
137138

138139
it('should abort previous async function when dependencies update', async () => {
139-
let hookState: any;
140+
let hookState!: AbortableAsyncState<unknown>;
140141
let aborted = false;
141142
const asyncFn = ({ signal }: { signal: AbortSignal }) =>
142143
new Promise<string>(() => {
@@ -204,7 +205,7 @@ describe('useAbortableAsync', () => {
204205
});
205206

206207
it('should not abort running promise when rerendered without dependency change', async () => {
207-
let hookState: any;
208+
let hookState!: AbortableAsyncState<unknown>;
208209
let aborted = false;
209210
const asyncFn = ({ signal }: { signal: AbortSignal }) =>
210211
new Promise<string>(() => {

src/platform/packages/shared/kbn-react-hooks/src/use_abortable_async/use_abortable_async.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import { isPromise } from '@kbn/std';
11+
import type { DependencyList } from 'react';
1112
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1213

1314
interface State<T> {
@@ -20,7 +21,7 @@ export type AbortableAsyncState<T> = (T extends Promise<infer TReturn>
2021
? State<TReturn>
2122
: State<T>) & { refresh: () => void };
2223

23-
export type AbortableAsyncStateOf<T extends AbortableAsyncState<any>> =
24+
export type AbortableAsyncStateOf<T extends AbortableAsyncState<unknown>> =
2425
T extends AbortableAsyncState<infer TResponse> ? Awaited<TResponse> : never;
2526

2627
export interface UseAbortableAsyncOptions<T> {
@@ -31,17 +32,17 @@ export interface UseAbortableAsyncOptions<T> {
3132
}
3233

3334
export type UseAbortableAsync<
34-
TAdditionalParameters extends Record<string, any> = {},
35-
TAdditionalOptions extends Record<string, any> = {}
35+
TAdditionalParameters extends Record<string, unknown> = Record<string, never>,
36+
TAdditionalOptions extends Record<string, unknown> = Record<string, never>
3637
> = <T>(
3738
fn: ({}: { signal: AbortSignal } & TAdditionalParameters) => T | Promise<T>,
38-
deps: any[],
39+
deps: DependencyList,
3940
options?: UseAbortableAsyncOptions<T> & TAdditionalOptions
4041
) => AbortableAsyncState<T>;
4142

4243
export function useAbortableAsync<T>(
4344
fn: ({}: { signal: AbortSignal }) => T | Promise<T>,
44-
deps: any[],
45+
deps: DependencyList,
4546
options?: UseAbortableAsyncOptions<T>
4647
): AbortableAsyncState<T> {
4748
const clearValueOnNext = options?.clearValueOnNext;

src/platform/packages/shared/kbn-react-hooks/src/use_debounce_fn/use_debounce_fn.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import { useMemo } from 'react';
1212
import useLatest from 'react-use/lib/useLatest';
1313
import useUnmount from 'react-use/lib/useUnmount';
1414

15-
type Noop = (...args: any[]) => any;
16-
1715
export interface DebounceOptions {
1816
wait?: number;
1917
leading?: boolean;
@@ -38,7 +36,10 @@ export interface DebounceOptions {
3836
* @caveat The debounce does not cancel if `options` or `wait` are changed between calls.
3937
*/
4038

41-
export function useDebounceFn<T extends Noop>(fn: T, options?: DebounceOptions) {
39+
export function useDebounceFn<A extends unknown[], R>(
40+
fn: (...args: A) => R,
41+
options?: DebounceOptions
42+
) {
4243
if (!isFunction(fn)) {
4344
throw Error(`useDebounceFn expected parameter is a function, got ${typeof fn}`);
4445
}
@@ -50,7 +51,7 @@ export function useDebounceFn<T extends Noop>(fn: T, options?: DebounceOptions)
5051
const debounced = useMemo(
5152
() =>
5253
debounce(
53-
(...args: Parameters<T>): ReturnType<T> => {
54+
(...args: A): R => {
5455
return fnRef.current(...args);
5556
},
5657
wait,

src/platform/packages/shared/kbn-react-hooks/src/use_throttle_fn/use_throttle_fn.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import { useMemo } from 'react';
1212
import useLatest from 'react-use/lib/useLatest';
1313
import useUnmount from 'react-use/lib/useUnmount';
1414

15-
type Noop = (...args: any[]) => any;
16-
1715
export interface ThrottleOptions {
1816
wait?: number;
1917
leading?: boolean;
@@ -35,7 +33,10 @@ export interface ThrottleOptions {
3533
*
3634
* @caveat The throttle does not cancel if `options` or `wait` are changed between calls.
3735
*/
38-
export function useThrottleFn<T extends Noop>(fn: T, options?: ThrottleOptions) {
36+
export function useThrottleFn<A extends unknown[], R>(
37+
fn: (...args: A) => R,
38+
options?: ThrottleOptions
39+
) {
3940
if (!isFunction(fn)) {
4041
throw Error(`useThrottleFn expected parameter is a function, got ${typeof fn}`);
4142
}
@@ -47,7 +48,7 @@ export function useThrottleFn<T extends Noop>(fn: T, options?: ThrottleOptions)
4748
const throttled = useMemo(
4849
() =>
4950
throttle(
50-
(...args: Parameters<T>): ReturnType<T> => {
51+
(...args: A): R => {
5152
return fnRef.current(...args);
5253
},
5354
wait,

0 commit comments

Comments
 (0)