-
-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
Add generics for Remix type enhancements #10843
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
"react-router-dom": patch | ||
"react-router": patch | ||
"@remix-run/router": patch | ||
--- | ||
|
||
In order to move towards stricter TypeScript support in the future, we're aiming to replace current usages of `any` with `unknown` on exposed typings for user-provided data. To do this in Remix v2 without introducing breaking changes in React Router v6, we have added generics to a number of shared types. These continue to default to `any` in React Router and are overridden with `unknown` in Remix. In React Router v7 we plan to move these to `unknown` as a breakjing change. | ||
|
||
- `Location` now accepts a generic for the `location.state` value | ||
- `ActionFunctionArgs`/`ActionFunction`/`LoaderFunctionArgs`/`LoaderFunction` now accept a generic for the `context` parameter (only used in SSR usages via `createStaticHandler`) | ||
- The return type of `useMatches` (now exported as `UIMatch`) accepts generics for `match.data` and `match.handle` - both of which were already set to `unknown` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,6 @@ import type { | |
ActionFunction, | ||
AgnosticDataRouteMatch, | ||
AgnosticDataRouteObject, | ||
AgnosticRouteMatch, | ||
AgnosticRouteObject, | ||
DataResult, | ||
DeferredData, | ||
|
@@ -31,12 +30,14 @@ import type { | |
ShouldRevalidateFunctionArgs, | ||
Submission, | ||
SuccessResult, | ||
UIMatch, | ||
V7_FormMethod, | ||
V7_MutationFormMethod, | ||
} from "./utils"; | ||
import { | ||
ErrorResponseImpl, | ||
ResultType, | ||
convertRouteMatchToUiMatch, | ||
convertRoutesToDataRoutes, | ||
getPathContributingMatches, | ||
immutableRouteKeys, | ||
|
@@ -394,20 +395,12 @@ export interface RouterSubscriber { | |
(state: RouterState): void; | ||
} | ||
|
||
interface UseMatchesMatch { | ||
id: string; | ||
pathname: string; | ||
params: AgnosticRouteMatch["params"]; | ||
data: unknown; | ||
handle: unknown; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now exported from |
||
|
||
/** | ||
* Function signature for determining the key to be used in scroll restoration | ||
* for a given location | ||
*/ | ||
export interface GetScrollRestorationKeyFunction { | ||
(location: Location, matches: UseMatchesMatch[]): string | null; | ||
(location: Location, matches: UIMatch[]): string | null; | ||
} | ||
|
||
/** | ||
|
@@ -2461,7 +2454,7 @@ export function createRouter(init: RouterInit): Router { | |
if (getScrollRestorationKey) { | ||
let key = getScrollRestorationKey( | ||
location, | ||
matches.map((m) => createUseMatchesMatch(m, state.loaderData)) | ||
matches.map((m) => convertRouteMatchToUiMatch(m, state.loaderData)) | ||
); | ||
return key || location.key; | ||
} | ||
|
@@ -4332,22 +4325,6 @@ function hasNakedIndexQuery(search: string): boolean { | |
return new URLSearchParams(search).getAll("index").some((v) => v === ""); | ||
} | ||
|
||
// Note: This should match the format exported by useMatches, so if you change | ||
// this please also change that :) Eventually we'll DRY this up | ||
function createUseMatchesMatch( | ||
match: AgnosticDataRouteMatch, | ||
loaderData: RouteData | ||
): UseMatchesMatch { | ||
let { route, pathname, params } = match; | ||
return { | ||
id: route.id, | ||
pathname, | ||
params, | ||
data: loaderData[route.id] as unknown, | ||
handle: route.handle as unknown, | ||
}; | ||
} | ||
|
||
function getTargetMatch( | ||
matches: AgnosticDataRouteMatch[], | ||
location: Location | string | ||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -137,21 +137,24 @@ export type Submission = | |||||||||
* Arguments passed to route loader/action functions. Same for now but we keep | ||||||||||
* this as a private implementation detail in case they diverge in the future. | ||||||||||
*/ | ||||||||||
interface DataFunctionArgs { | ||||||||||
interface DataFunctionArgs<Context> { | ||||||||||
request: Request; | ||||||||||
params: Params; | ||||||||||
context?: any; | ||||||||||
context?: Context; | ||||||||||
} | ||||||||||
|
||||||||||
// TODO: (v7) Change the defaults from any to unknown in and remove Remix wrappers: | ||||||||||
// ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs | ||||||||||
|
||||||||||
/** | ||||||||||
* Arguments passed to loader functions | ||||||||||
*/ | ||||||||||
export interface LoaderFunctionArgs extends DataFunctionArgs {} | ||||||||||
export interface LoaderFunctionArgs<C = any> extends DataFunctionArgs<C> {} | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
/** | ||||||||||
* Arguments passed to action functions | ||||||||||
*/ | ||||||||||
export interface ActionFunctionArgs extends DataFunctionArgs {} | ||||||||||
export interface ActionFunctionArgs<C = any> extends DataFunctionArgs<C> {} | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
||||||||||
/** | ||||||||||
* Loaders and actions can return anything except `undefined` (`null` is a | ||||||||||
|
@@ -163,15 +166,15 @@ type DataFunctionValue = Response | NonNullable<unknown> | null; | |||||||||
/** | ||||||||||
* Route loader function signature | ||||||||||
*/ | ||||||||||
export interface LoaderFunction { | ||||||||||
(args: LoaderFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue; | ||||||||||
export interface LoaderFunction<C = any> { | ||||||||||
(args: LoaderFunctionArgs<C>): Promise<DataFunctionValue> | DataFunctionValue; | ||||||||||
Comment on lines
+169
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Route action function signature | ||||||||||
*/ | ||||||||||
export interface ActionFunction { | ||||||||||
(args: ActionFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue; | ||||||||||
export interface ActionFunction<C = any> { | ||||||||||
(args: ActionFunctionArgs<C>): Promise<DataFunctionValue> | DataFunctionValue; | ||||||||||
Comment on lines
+176
to
+177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
|
@@ -490,6 +493,28 @@ export function matchRoutes< | |||||||||
return matches; | ||||||||||
} | ||||||||||
|
||||||||||
export interface UIMatch<D = unknown, H = unknown> { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
id: string; | ||||||||||
pathname: string; | ||||||||||
params: AgnosticRouteMatch["params"]; | ||||||||||
data: D; | ||||||||||
handle: H; | ||||||||||
Comment on lines
+500
to
+501
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
export function convertRouteMatchToUiMatch( | ||||||||||
match: AgnosticDataRouteMatch, | ||||||||||
loaderData: RouteData | ||||||||||
): UIMatch { | ||||||||||
let { route, pathname, params } = match; | ||||||||||
return { | ||||||||||
id: route.id, | ||||||||||
pathname, | ||||||||||
params, | ||||||||||
data: loaderData[route.id], | ||||||||||
handle: route.handle, | ||||||||||
}; | ||||||||||
} | ||||||||||
|
||||||||||
interface RouteMeta< | ||||||||||
RouteObjectType extends AgnosticRouteObject = AgnosticRouteObject | ||||||||||
> { | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.