Skip to content

Commit

Permalink
ref(types): add default type parameters for RouteComponentProps (#85139)
Browse files Browse the repository at this point in the history
giving the type RouteComponentProps safe default values for `Params` and
`RouteParams` avoids us having to always pass them where we don't know /
don't want to restrict us to specific params, which is the case in many
places. Oftentimes, the type `{}` was used, which is an unsafe type that
we're trying to get rid of and forbid its usage with eslint

With those changes, this PR gets the usages of the empty object type
down to 275 from the initial starting point of 465.

---

The most relevant changes are:

```diff
- export interface RouteComponentProps<P, R, ComponentProps = any, Q = any> {
+ export interface RouteComponentProps<
+   P = Record<string, string | undefined>,
+   R = Record<string, string | undefined>,
+   ComponentProps = any,
+   Q = any,
+ > {
```

`Record<string, string | undefined>` is a “safe” default value, as it
means all params that components get access to from the router can be
read in a potentially undefined way.

Usages where we know parameters exist because of route matching can
still overwrite this default, which is what we frequently do, e.g.:

```
type Props = RouteComponentProps<{orgId: string}>;
```

In this cases, `orgId` can be read as type string, and it will also be
the only key that can be read from `params`.

some other interfaces had to be widened per default from `Record<string,
string>` to `Record<string, string | undefined>` so that they are
compatible:

```diff
- export interface WithRouterProps<P = Record<string, string>, Q = any> {
+ export interface WithRouterProps<P = Record<string, string | undefined>, Q = any> {
```

```diff
- export interface RouteContextInterface<P = Record<string, string>, Q = any> {
+ export interface RouteContextInterface<P = Record<string, string | undefined>, Q = any> {
```

This change didn’t yield any type errors, but it will make future access
more safe.
  • Loading branch information
TkDodo authored Feb 14, 2025
1 parent 03c8a83 commit 3f3df6d
Show file tree
Hide file tree
Showing 148 changed files with 199 additions and 218 deletions.
2 changes: 1 addition & 1 deletion static/app/components/deprecatedAsyncComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
import PermissionDenied from 'sentry/views/permissionDenied';
import RouteError from 'sentry/views/routeError';

export interface AsyncComponentProps extends Partial<RouteComponentProps<{}, {}>> {}
export interface AsyncComponentProps extends Partial<RouteComponentProps> {}

export interface AsyncComponentState {
[key: string]: any;
Expand Down
2 changes: 1 addition & 1 deletion static/app/components/search/sources/routeSource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type DefaultProps = {
searchOptions: Fuse.IFuseOptions<NavigationItem>;
};

type Props = RouteComponentProps<{}, {}> &
type Props = RouteComponentProps &
DefaultProps & {
/**
* Render function that renders the route matches
Expand Down
2 changes: 1 addition & 1 deletion static/app/types/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ type DateRangeProps = React.ComponentProps<typeof DateRange>;

type SelectorItemsProps = React.ComponentProps<typeof SelectorItems>;

type DisabledMemberViewProps = RouteComponentProps<{orgId: string}, {}>;
type DisabledMemberViewProps = RouteComponentProps<{orgId: string}>;

type MemberListHeaderProps = {
members: Member[];
Expand Down
15 changes: 10 additions & 5 deletions static/app/types/legacyReactRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,14 @@ export interface PlainRoute<Props = any> extends RouteProps<Props> {
indexRoute?: PlainRoute | undefined;
}

export interface RouteComponentProps<P, R, ComponentProps = any, Q = any> {
export interface RouteComponentProps<
P = Record<string, string | undefined>,
R = Record<string, string | undefined>,
ComponentProps = any,
Q = any,
> {
location: Location<Q>;
params: P & R;
params: P;
route: PlainRoute<ComponentProps>;
routeParams: R;
router: InjectedRouter;
Expand All @@ -100,7 +105,7 @@ type ActiveFunction = (location: LocationDescriptor, indexOnly?: boolean) => boo
type LeaveHookFunction = (route: any, callback: RouteHook) => () => void;
type CreatePartFunction<Part> = (pathOrLoc: LocationDescriptor, query?: any) => Part;

export interface InjectedRouter<P = Record<string, string>, Q = any> {
export interface InjectedRouter<P = Record<string, string | undefined>, Q = any> {
createHref: CreatePartFunction<Href>;
createPath: CreatePartFunction<Path>;
go: GoFunction;
Expand All @@ -115,14 +120,14 @@ export interface InjectedRouter<P = Record<string, string>, Q = any> {
setRouteLeaveHook: LeaveHookFunction;
}

export interface WithRouterProps<P = Record<string, string>, Q = any> {
export interface WithRouterProps<P = Record<string, string | undefined>, Q = any> {
location: Location<Q>;
params: P;
router: InjectedRouter<P, Q>;
routes: PlainRoute[];
}

export interface RouteContextInterface<P = Record<string, string>, Q = any> {
export interface RouteContextInterface<P = Record<string, string | undefined>, Q = any> {
location: Location<Q>;
params: P;
router: InjectedRouter<P, Q>;
Expand Down
2 changes: 1 addition & 1 deletion static/app/utils/withDomainRedirect.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const projectRoutes = [
];

describe('withDomainRedirect', function () {
type Props = RouteComponentProps<{orgId: string}, {}>;
type Props = RouteComponentProps<{orgId: string}>;
function MyComponent(props: Props) {
const {params} = props;
return <div>Org slug: {params.orgId ?? 'no org slug'}</div>;
Expand Down
3 changes: 1 addition & 2 deletions static/app/utils/withDomainRedirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import useOrganization from './useOrganization';
* If either a customer domain is not being used, or if :orgId is not present in the route path, then WrappedComponent
* is rendered.
*/
function withDomainRedirect<P extends RouteComponentProps<{}, {}>>(
function withDomainRedirect<P extends RouteComponentProps>(
WrappedComponent: RouteComponent
) {
return function WithDomainRedirectWrapper(props: P) {
Expand Down Expand Up @@ -60,7 +60,6 @@ function withDomainRedirect<P extends RouteComponentProps<{}, {}>>(
// Regenerate the full route with the :orgId parameter omitted.
const newParams = {...params};
Object.keys(params).forEach(param => {
// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
newParams[param] = `:${param}`;
});
const fullRoute = recreateRoute('', {routes, params: newParams});
Expand Down
2 changes: 1 addition & 1 deletion static/app/utils/withDomainRequired.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import withDomainRequired from 'sentry/utils/withDomainRequired';
const originalLocation = window.location;

describe('withDomainRequired', function () {
type Props = RouteComponentProps<{orgId: string}, {}>;
type Props = RouteComponentProps<{orgId: string}>;
function MyComponent(props: Props) {
const {params} = props;
return <div>Org slug: {params.orgId ?? 'no org slug'}</div>;
Expand Down
2 changes: 1 addition & 1 deletion static/app/utils/withDomainRequired.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type {RouteComponent, RouteComponentProps} from 'sentry/types/legacyReact
*
* Whenever https://orgslug.sentry.io/ is accessed in the browser, then both conditions above will be satisfied.
*/
export default function withDomainRequired<P extends RouteComponentProps<{}, {}>>(
export default function withDomainRequired<P extends RouteComponentProps>(
WrappedComponent: RouteComponent
) {
return function withDomainRequiredWrapper(props: P) {
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/acceptProjectTransfer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {Organization} from 'sentry/types/organization';
import type {Project} from 'sentry/types/project';
import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';

type Props = RouteComponentProps<{}, {}>;
type Props = RouteComponentProps;

type TransferDetails = {
organizations: Organization[];
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/admin/adminLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const renderAdminNavigation = () => (

type Props = {
children: React.ReactNode;
} & RouteComponentProps<{}, {}>;
} & RouteComponentProps;

function AdminLayout({children, ...props}: Props) {
return (
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/admin/adminOrganizations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ResultGrid from 'sentry/components/resultGrid';
import {t} from 'sentry/locale';
import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';

type Props = RouteComponentProps<{}, {}>;
type Props = RouteComponentProps;

const getRow = (row: any) => [
<td key={row.id}>
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/admin/adminProjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Row = {
status: string;
};

type Props = RouteComponentProps<{}, {}>;
type Props = RouteComponentProps;

const getRow = (row: Row) => [
<td key="name">
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/admin/adminRelays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import withApi from 'sentry/utils/withApi';

const prettyDate = (x: string) => moment(x).format('ll LTS');

type Props = RouteComponentProps<{}, {}> & {api: Client};
type Props = RouteComponentProps & {api: Client};

type State = {
loading: boolean;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/admin/adminUserEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class RemoveUserModal extends Component<RemoveModalProps, RemoveModalState> {
}
}

type Props = DeprecatedAsyncComponent['props'] & RouteComponentProps<{id: string}, {}>;
type Props = DeprecatedAsyncComponent['props'] & RouteComponentProps<{id: string}>;

type State = DeprecatedAsyncComponent['state'] & {
user: User | null;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/admin/adminUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Row = {
username: string;
};

type Props = RouteComponentProps<{}, {}>;
type Props = RouteComponentProps;

const getRow = (row: Row) => [
<td key="username">
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/builder/projectProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {useIsMountedRef} from 'sentry/utils/useIsMountedRef';
import useProjects from 'sentry/utils/useProjects';
import useScrollToTop from 'sentry/utils/useScrollToTop';

type Props = RouteComponentProps<RouteParams, {}> & {
type Props = RouteComponentProps<RouteParams> & {
hasMetricAlerts: boolean;
organization: Organization;
children?: React.ReactNode;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type RouteParams = {
projectId?: string;
};

type Props = RouteComponentProps<RouteParams, {}> & {
type Props = RouteComponentProps<RouteParams> & {
hasMetricAlerts: boolean;
members: Member[] | undefined;
organization: Organization;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type RouteParams = {
ruleId: string;
};

type Props = RouteComponentProps<RouteParams, {}> & {
type Props = RouteComponentProps<RouteParams> & {
hasMetricAlerts: boolean;
hasUptimeAlerts: boolean;
members: Member[] | undefined;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/incidentRedirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {alertDetailsLink} from './utils';

type Props = {
organization: Organization;
} & RouteComponentProps<{alertId: string}, {}>;
} & RouteComponentProps<{alertId: string}>;

/**
* Reirects from an incident to the incident's metric alert details page
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/list/incidents/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import AlertListRow from './row';
const DOCS_URL =
'https://docs.sentry.io/workflow/alerts-notifications/alerts/?_ga=2.21848383.580096147.1592364314-1444595810.1582160976';

type Props = RouteComponentProps<{}, {}> & {
type Props = RouteComponentProps & {
organization: Organization;
};

Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/crons/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import {makeMonitorDetailsQueryKey} from 'sentry/views/monitors/utils';

const DEFAULT_POLL_INTERVAL_MS = 5000;

type Props = RouteComponentProps<{monitorSlug: string; projectId: string}, {}>;
type Props = RouteComponentProps<{monitorSlug: string; projectId: string}>;

function hasLastCheckIn(monitor: Monitor) {
return monitor.environments.some(e => e.lastCheckIn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import AlertRuleIssuesList from './issuesList';
import Sidebar from './sidebar';

interface AlertRuleDetailsProps
extends RouteComponentProps<{projectId: string; ruleId: string}, {}> {}
extends RouteComponentProps<{projectId: string; ruleId: string}> {}

const PAGE_QUERY_PARAMS = [
'pageStatsPeriod',
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/issue/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ type Props = {
userTeamIds: string[];
loadingProjects?: boolean;
onChangeTitle?: (data: string) => void;
} & RouteComponentProps<RouteParams, {}>;
} & RouteComponentProps<RouteParams>;

type State = DeprecatedAsyncComponent['state'] & {
configs: IssueAlertConfiguration | null;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/metric/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type Props = {
userTeamIds: string[];
sessionId?: string;
wizardTemplate?: WizardRuleTemplate;
} & RouteComponentProps<RouteParams, {}>;
} & RouteComponentProps<RouteParams>;

/**
* Show metric rules form with an empty rule. Redirects to alerts list after creation.
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/metric/details/body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import RelatedTransactions from './relatedTransactions';
import {MetricDetailsSidebar} from './sidebar';
import {getFilter, getPeriodInterval} from './utils';

export interface MetricDetailsBodyProps extends RouteComponentProps<{}, {}> {
export interface MetricDetailsBodyProps extends RouteComponentProps {
api: Client;
location: Location;
organization: Organization;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/metric/details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {ALERT_RULE_STATUS, TIME_OPTIONS, TIME_WINDOWS} from './constants';
import DetailsHeader from './header';
import {buildMetricGraphDateRange} from './utils';

interface Props extends RouteComponentProps<{ruleId: string}, {}> {
interface Props extends RouteComponentProps<{ruleId: string}> {
api: Client;
location: Location;
organization: Organization;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/metric/duplicate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type {WizardRuleTemplate} from 'sentry/views/alerts/wizard/options';

import RuleForm from './ruleForm';

interface MetricRuleDuplicateProps extends RouteComponentProps<{}, {}> {
interface MetricRuleDuplicateProps extends RouteComponentProps {
project: Project;
userTeamIds: string[];
eventView?: EventView;
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/metric/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type Props = {
organization: Organization;
project: Project;
userTeamIds: string[];
} & RouteComponentProps<RouteParams, {}>;
} & RouteComponentProps<RouteParams>;

export function MetricRulesEdit({
organization,
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/metric/ruleForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ type Props = {
isDuplicateRule?: boolean;
ruleId?: string;
sessionId?: string;
} & RouteComponentProps<{projectId?: string; ruleId?: string}, {}> & {
} & RouteComponentProps<{projectId?: string; ruleId?: string}> & {
onSubmitSuccess?: FormProps['onSubmitSuccess'];
} & DeprecatedAsyncComponent['props'];

Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/uptime/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {UptimeChecksTable} from './uptimeChecksTable';
import {UptimeIssues} from './uptimeIssues';

interface UptimeAlertDetailsProps
extends RouteComponentProps<{projectId: string; uptimeRuleId: string}, {}> {}
extends RouteComponentProps<{projectId: string; uptimeRuleId: string}> {}

export default function UptimeAlertDetails({params}: UptimeAlertDetailsProps) {
const api = useApi();
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/rules/uptime/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type Props = {
organization: Organization;
project: Project;
userTeamIds: string[];
} & RouteComponentProps<RouteParams, {}>;
} & RouteComponentProps<RouteParams>;

export function UptimeRulesEdit({params, onChangeTitle, organization, project}: Props) {
const api = useApi();
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/alerts/wizard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type RouteParams = {
projectId?: string;
};

type AlertWizardProps = RouteComponentProps<RouteParams, {}> & {
type AlertWizardProps = RouteComponentProps<RouteParams> & {
organization: Organization;
projectId: string;
};
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {DEMO_HEADER_HEIGHT_PX} from '../../components/demo/demoHeader';

type Props = {
children: React.ReactNode;
} & RouteComponentProps<{orgId?: string}, {}>;
} & RouteComponentProps<{orgId?: string}>;

const InstallWizard = lazy(
() => import('sentry/views/admin/installWizard')
Expand Down
2 changes: 1 addition & 1 deletion static/app/views/auth/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type ActiveTab = keyof typeof FORM_COMPONENTS;

type TabConfig = [key: ActiveTab, label: string, disabled?: boolean];

type Props = RouteComponentProps<{orgId?: string}, {}> & {
type Props = RouteComponentProps<{orgId?: string}> & {
api: Client;
};

Expand Down
2 changes: 1 addition & 1 deletion static/app/views/dashboards/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {Widget} from './types';
import {DashboardState} from './types';
import {cloneDashboard, constructWidgetFromQuery} from './utils';

type Props = RouteComponentProps<{templateId?: string; widgetId?: string}, {}> & {
type Props = RouteComponentProps<{templateId?: string; widgetId?: string}> & {
children: React.ReactNode;
organization: Organization;
};
Expand Down
Loading

0 comments on commit 3f3df6d

Please sign in to comment.