Skip to content

feat: Avoid usage of any #17041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
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
5 changes: 2 additions & 3 deletions packages/core/src/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ export function instrumentFetchRequest(
spans[span.spanContext().spanId] = span;

if (shouldAttachHeaders(handlerData.fetchData.url)) {
const request: string | Request = handlerData.args[0];

const options: { [key: string]: unknown } = handlerData.args[1] || {};
const request = handlerData.args[0] as string | Request;
const options = (handlerData.args[1] || {}) as Record<string, unknown>;

const headers = _addTracingHeadersToFetchRequest(
request,
Expand Down
33 changes: 21 additions & 12 deletions packages/core/src/integrations/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { defineIntegration } from '../integration';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../semanticAttributes';
import { setHttpStatus, SPAN_STATUS_ERROR, SPAN_STATUS_OK, startSpan } from '../tracing';
import type { IntegrationFn } from '../types-hoist/integration';
import type { SpanAttributes } from '../types-hoist/span';
import { debug } from '../utils/debug-logger';
import { isPlainObject } from '../utils/is';

Expand Down Expand Up @@ -64,7 +65,8 @@ export const FILTER_MAPPINGS = {
not: 'not',
};

export const DB_OPERATIONS_TO_INSTRUMENT = ['select', 'insert', 'upsert', 'update', 'delete'];
export const DB_OPERATIONS_TO_INSTRUMENT = ['select', 'insert', 'upsert', 'update', 'delete'] as const;
type DBOperation = (typeof DB_OPERATIONS_TO_INSTRUMENT)[number];

type AuthOperationFn = (...args: unknown[]) => Promise<unknown>;
type AuthOperationName = (typeof AUTH_OPERATIONS_TO_INSTRUMENT)[number];
Expand All @@ -86,7 +88,7 @@ export interface PostgRESTFilterBuilder {
headers: Record<string, string>;
url: URL;
schema: string;
body: any;
body: unknown;
}

export interface SupabaseResponse {
Expand Down Expand Up @@ -122,7 +124,7 @@ export interface SupabaseClientConstructor {
export interface PostgRESTProtoThenable {
then: <T>(
onfulfilled?: ((value: T) => T | PromiseLike<T>) | null,
onrejected?: ((reason: any) => T | PromiseLike<T>) | null,
onrejected?: ((reason: unknown) => T | PromiseLike<T>) | null,
) => Promise<T>;
}

Expand Down Expand Up @@ -152,7 +154,7 @@ function isInstrumented<T>(fn: T): boolean | undefined {
* @param headers - The request headers
* @returns The database operation type ('select', 'insert', 'upsert', 'update', or 'delete')
*/
export function extractOperation(method: string, headers: Record<string, string> = {}): string {
export function extractOperation(method: string, headers: Record<string, string> = {}): DBOperation | '<unknown-op>' {
switch (method) {
case 'GET': {
return 'select';
Expand Down Expand Up @@ -333,7 +335,7 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
const typedThis = thisArg as PostgRESTFilterBuilder;
const operation = extractOperation(typedThis.method, typedThis.headers);

if (!operations.includes(operation)) {
if (!operations.includes(operation as DBOperation)) {
return Reflect.apply(target, thisArg, argumentsList);
}

Expand Down Expand Up @@ -364,7 +366,7 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
' ',
)} from(${table})`;

const attributes: Record<string, any> = {
const attributes: SpanAttributes = {
'db.table': table,
'db.schema': typedThis.schema,
'db.url': typedThis.url.origin,
Expand All @@ -380,7 +382,11 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
}

if (Object.keys(body).length) {
attributes['db.body'] = body;
try {
attributes['db.body'] = JSON.stringify(body);
Copy link
Member

Choose a reason for hiding this comment

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

q: Did this get stringified anyway before or why are we doing it now?

Copy link
Member Author

Choose a reason for hiding this comment

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

not sure if this ever worked, but OTEL does not support complex attributes. Maybe it kind-of worked because we accept this in our transaction event, but this is the more safe/correct solution IMHO!

} catch {
// could not stringify body
}
}

return startSpan(
Expand Down Expand Up @@ -465,17 +471,19 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
markAsInstrumented((PostgRESTFilterBuilder.prototype as unknown as PostgRESTProtoThenable).then);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type BuilderPrototype = Record<DBOperation, (...args: any[]) => any>;

function instrumentPostgRESTQueryBuilder(PostgRESTQueryBuilder: new () => PostgRESTQueryBuilder): void {
// We need to wrap _all_ operations despite them sharing the same `PostgRESTFilterBuilder`
// constructor, as we don't know which method will be called first, and we don't want to miss any calls.
for (const operation of DB_OPERATIONS_TO_INSTRUMENT) {
if (isInstrumented((PostgRESTQueryBuilder.prototype as Record<string, any>)[operation])) {
if (isInstrumented((PostgRESTQueryBuilder.prototype as BuilderPrototype)[operation])) {
continue;
}

type PostgRESTOperation = keyof Pick<PostgRESTQueryBuilder, 'select' | 'insert' | 'upsert' | 'update' | 'delete'>;
(PostgRESTQueryBuilder.prototype as Record<string, any>)[operation as PostgRESTOperation] = new Proxy(
(PostgRESTQueryBuilder.prototype as Record<string, any>)[operation as PostgRESTOperation],
(PostgRESTQueryBuilder.prototype as BuilderPrototype)[operation] = new Proxy(
(PostgRESTQueryBuilder.prototype as BuilderPrototype)[operation],
{
apply(target, thisArg, argumentsList) {
const rv = Reflect.apply(target, thisArg, argumentsList);
Expand All @@ -490,7 +498,7 @@ function instrumentPostgRESTQueryBuilder(PostgRESTQueryBuilder: new () => PostgR
},
);

markAsInstrumented((PostgRESTQueryBuilder.prototype as Record<string, any>)[operation]);
markAsInstrumented((PostgRESTQueryBuilder.prototype as BuilderPrototype)[operation]);
}
}

Expand All @@ -517,6 +525,7 @@ const _supabaseIntegration = ((supabaseClient: unknown) => {
};
}) satisfies IntegrationFn;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const supabaseIntegration = defineIntegration((options: { supabaseClient: any }) => {
return _supabaseIntegration(options.supabaseClient);
}) satisfies IntegrationFn;
1 change: 1 addition & 0 deletions packages/core/src/profiling.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Client } from './client';
import { getClient } from './currentScopes';
import { DEBUG_BUILD } from './debug-build';
import type { Profiler, ProfilingIntegration } from './types-hoist/profiling';
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/types-hoist/breadcrumb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export interface Breadcrumb {
*
* @summary Arbitrary data associated with this breadcrumb.
*/
data?: { [key: string]: any };
// Note: we cannot use Record<string, unknown> here because it's not compatible with interface data types
// See: https://github.com/microsoft/TypeScript/issues/15300
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data?: Record<string, any>;

/**
* The format is a numeric (integer or float) value representing
Expand All @@ -70,6 +73,9 @@ export interface Breadcrumb {

/** JSDoc */
export interface BreadcrumbHint {
// Note: we cannot use unknown here because it's not compatible with interface data types
// See: https://github.com/microsoft/TypeScript/issues/15300
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
}

Expand All @@ -90,7 +96,7 @@ export interface XhrBreadcrumbData {
}

export interface FetchBreadcrumbHint {
input: any[];
input: unknown[];
data?: unknown;
response?: unknown;
startTimestamp: number;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/types-hoist/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FeatureFlag } from '../utils/featureFlags';
import type { SpanLinkJSON } from './link';
import type { Primitive } from './misc';
import type { SpanOrigin } from './span';
import type { SpanAttributes, SpanOrigin } from './span';

export type Context = Record<string, unknown>;

Expand Down Expand Up @@ -99,7 +99,7 @@ export interface ResponseContext extends Record<string, unknown> {
}

export interface TraceContext extends Record<string, unknown> {
data?: { [key: string]: any };
data?: SpanAttributes;
op?: string;
parent_span_id?: string;
span_id: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/types-hoist/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
* Just an Error object with arbitrary attributes attached to it.
*/
export interface ExtendedError extends Error {
[key: string]: any;
[key: string]: unknown;
}
2 changes: 1 addition & 1 deletion packages/core/src/types-hoist/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,6 @@ export interface EventHint {
syntheticException?: Error | null;
originalException?: unknown;
attachments?: Attachment[];
data?: any;
data?: unknown;
integrations?: string[];
}
5 changes: 3 additions & 2 deletions packages/core/src/types-hoist/feedback/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Primitive } from '../misc';
import type { User } from '../user';
import type { FeedbackFormData } from './form';
import type { FeedbackTheme } from './theme';

Expand Down Expand Up @@ -53,8 +54,8 @@ export interface FeedbackGeneralConfiguration {
* The value of the email/name keys represent the properties of your user context.
*/
useSentryUser: {
email: string;
name: string;
email: keyof User;
name: keyof User;
};

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/types-hoist/instrument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ interface SentryFetchData {
}

export interface HandlerDataFetch {
args: any[];
args: unknown[];
fetchData: SentryFetchData; // This data is among other things dumped directly onto the fetch breadcrumb data
startTimestamp: number;
endTimestamp?: number;
Expand All @@ -74,7 +74,7 @@ export interface HandlerDataDom {

export interface HandlerDataConsole {
level: ConsoleLevel;
args: any[];
args: unknown[];
}

export interface HandlerDataHistory {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/types-hoist/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ export interface Integration {
* An integration in function form.
* This is expected to return an integration.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Copy link
Member

Choose a reason for hiding this comment

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

Could we use unknown here?

Copy link
Member Author

Choose a reason for hiding this comment

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

this sadly does not work, there are some incompatibilites with function arguments and unknown, sometimes...

export type IntegrationFn<IntegrationType = Integration> = (...rest: any[]) => IntegrationType;
2 changes: 1 addition & 1 deletion packages/core/src/types-hoist/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { QueryParams } from './request';
* Data extracted from an incoming request to a node server
*/
export interface ExtractedNodeRequestData {
[key: string]: any;
[key: string]: unknown;

/** Specific headers from the request */
headers?: { [key: string]: string };
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/types-hoist/polymorphics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ type NextjsRequest = NodeRequest & {
[key: string]: string;
};
query?: {
[key: string]: any;
[key: string]: unknown;
};
};

type ExpressRequest = NodeRequest & {
baseUrl?: string;
body?: string | { [key: string]: any };
body?: string | { [key: string]: unknown };
host?: string;
hostname?: string;
ip?: string;
Expand All @@ -70,10 +70,10 @@ type ExpressRequest = NodeRequest & {
];
};
query?: {
[key: string]: any;
[key: string]: unknown;
};
user?: {
[key: string]: any;
[key: string]: unknown;
};
_reconstructedRoute?: string;
};
2 changes: 1 addition & 1 deletion packages/core/src/types-hoist/samplingcontext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { SpanAttributes } from './span';
* Context data passed by the user when starting a transaction, to be used by the tracesSampler method.
*/
export interface CustomSamplingContext {
[key: string]: any;
[key: string]: unknown;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/types-hoist/stackframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface StackFrame {
in_app?: boolean;
instruction_addr?: string;
addr_mode?: string;
vars?: { [key: string]: any };
vars?: { [key: string]: unknown };
debug_id?: string;
module_metadata?: any;
module_metadata?: unknown;
}
2 changes: 1 addition & 1 deletion packages/core/src/types-hoist/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* An interface describing a user of an application or a handled request.
*/
export interface User {
[key: string]: any;
[key: string]: unknown;
id?: string | number;
ip_address?: string | null;
email?: string;
Expand Down
11 changes: 6 additions & 5 deletions packages/core/src/utils/aggregate-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,17 @@ function aggregateExceptionsFromError(
let newExceptions = [...prevExceptions];

// Recursively call this function in order to walk down a chain of errors
if (isInstanceOf(error[key], Error)) {
const errorValue = error[key];
if (isInstanceOf(errorValue, Error)) {
applyExceptionGroupFieldsForParentException(exception, exceptionId);
const newException = exceptionFromErrorImplementation(parser, error[key]);
const newException = exceptionFromErrorImplementation(parser, errorValue);
const newExceptionId = newExceptions.length;
applyExceptionGroupFieldsForChildException(newException, key, newExceptionId, exceptionId);
newExceptions = aggregateExceptionsFromError(
exceptionFromErrorImplementation,
parser,
limit,
error[key],
errorValue as ExtendedError,
key,
[newException, ...newExceptions],
newException,
Expand All @@ -75,7 +76,7 @@ function aggregateExceptionsFromError(
// This will create exception grouping for AggregateErrors
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError
if (Array.isArray(error.errors)) {
error.errors.forEach((childError, i) => {
error.errors.forEach((childError: unknown, i) => {
if (isInstanceOf(childError, Error)) {
applyExceptionGroupFieldsForParentException(exception, exceptionId);
const newException = exceptionFromErrorImplementation(parser, childError);
Expand All @@ -85,7 +86,7 @@ function aggregateExceptionsFromError(
exceptionFromErrorImplementation,
parser,
limit,
childError,
childError as ExtendedError,
key,
[newException, ...newExceptions],
newException,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils/eventbuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function eventFromUnknownInput(
exception: unknown,
hint?: EventHint,
): Event {
const providedMechanism: Mechanism | undefined = hint?.data && (hint.data as { mechanism: Mechanism }).mechanism;
const providedMechanism = (hint?.data as { mechanism?: Mechanism })?.mechanism;
const mechanism: Mechanism = providedMechanism || {
handled: true,
type: 'generic',
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils/is.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export function isSyntheticEvent(wat: unknown): boolean {
* @param base A constructor to be used in a check.
* @returns A boolean representing the result.
*/
export function isInstanceOf(wat: any, base: any): boolean {
export function isInstanceOf<T extends new (...args: any) => any>(wat: any, base: T): wat is InstanceType<T> {
try {
return wat instanceof base;
} catch {
Expand Down
3 changes: 3 additions & 0 deletions packages/eslint-config-sdk/src/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ module.exports = {

// We do not care about empty functions
'@typescript-eslint/no-empty-function': 'off',

// Disallow (instead of warn) for usage of any.
'@typescript-eslint/no-explicit-any': 'error',
},
},
{
Expand Down
7 changes: 5 additions & 2 deletions packages/feedback/src/modal/integration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,17 @@ export const feedbackModalIntegration = ((): FeedbackModalIntegration => {
const screenshotInput = screenshotIntegration?.createInput({ h, hooks, dialog, options });

const renderContent = (open: boolean): void => {
const defaultName = user?.[userKey.name];
const defaultEmail = user?.[userKey.email];

render(
<Dialog
options={options}
screenshotInput={screenshotInput}
showName={options.showName || options.isNameRequired}
showEmail={options.showEmail || options.isEmailRequired}
defaultName={(userKey && user?.[userKey.name]) || ''}
defaultEmail={(userKey && user?.[userKey.email]) || ''}
defaultName={typeof defaultName === 'string' ? defaultName : ''}
defaultEmail={typeof defaultEmail === 'string' ? defaultEmail : ''}
onFormClose={() => {
renderContent(false);
options.onFormClose?.();
Expand Down
Loading
Loading