Skip to content

Instantly share code, notes, and snippets.

@swyxio
Last active October 4, 2022 00:36
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save swyxio/37a6a3d248c2d4031801f0d568904df8 to your computer and use it in GitHub Desktop.
Save swyxio/37a6a3d248c2d4031801f0d568904df8 to your computer and use it in GitHub Desktop.
react router dom v6 types - i have not tested this in all cases, nor have i ensured this covers the full api, - this should just be a nice drop in single .d.ts file that covers basic usecases detailed in https://dev.to/emreloper/react-router-v6-in-two-minutes-2i96, including inlining the necessary types for history and react-router
// // with thanks
// https://dev.to/emreloper/react-router-v6-in-two-minutes-2i96
// https://github.com/ReactTraining/react-router/blob/dev/docs/installation/getting-started.md
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/9d29adedf662de685356f711951ef8b9e8342865/types/react-router/index.d.ts#L1
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/9d29adedf662de685356f711951ef8b9e8342865/types/react-router-dom/index.d.ts#L1
// // release notes
// https://github.com/ReactTraining/react-router/releases/tag/v6.0.0-alpha.3
declare module "react-router-dom" {
import * as React from 'react';
/**
* react-router-dom
*/
// new in v6
type navigateFn<T> = (to: string | number, options?: NavigateOptions<T>) => void
export function useNavigate<T = Record<string, any>> (to: string | number, options?: Omit<NavigateProps<T>, 'to'>): navigateFn<T>;
type NavigateOptions<T> = Omit<NavigateProps<T>, 'to'>
type NavigateProps<T> = {
to: string | number,
replace?: boolean,
state?: T
}
export class Navigate<T = any> extends React.Component<NavigateProps<T>>{}
// existing api's
export interface BrowserRouterProps {
basename?: string;
getUserConfirmation?: (
message: string,
callback: (ok: boolean) => void
) => void;
forceRefresh?: boolean;
keyLength?: number;
}
export class BrowserRouter extends React.Component<BrowserRouterProps, any> {}
export interface StaticContext {
statusCode?: number;
}
export interface NavLinkProps<S = H.LocationState> extends LinkProps<S> {
activeClassName?: string;
activeStyle?: React.CSSProperties;
exact?: boolean;
strict?: boolean;
isActive?<Params extends { [K in keyof Params]?: string }>(
match: match<Params>,
location: H.Location<S>
): boolean;
location?: H.Location<S>;
}
export class NavLink<S = H.LocationState> extends React.Component<
NavLinkProps<S>,
any
> {}
export interface LinkProps<S = H.LocationState> extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
component?: React.ComponentType<any>;
to: H.LocationDescriptor<S> | ((location: H.Location<S>) => H.LocationDescriptor<S>);
replace?: boolean;
innerRef?: React.Ref<HTMLAnchorElement>;
}
export class Link<S = H.LocationState> extends React.Component<
LinkProps<S>,
any
> {}
// new in v6
export class Outlet extends React.Component {
props: {}; // denying props or children!
}
/**
* history
*/
export namespace H {
export type Action = 'PUSH' | 'POP' | 'REPLACE';
export type UnregisterCallback = () => void;
export interface History<HistoryLocationState = LocationState> {
length: number;
action: Action;
location: Location<HistoryLocationState>;
push(path: Path, state?: HistoryLocationState): void;
push(location: LocationDescriptorObject<HistoryLocationState>): void;
replace(path: Path, state?: HistoryLocationState): void;
replace(location: LocationDescriptorObject<HistoryLocationState>): void;
go(n: number): void;
goBack(): void;
goForward(): void;
block(
prompt?: boolean | string | TransitionPromptHook<HistoryLocationState>,
): UnregisterCallback;
listen(listener: LocationListener<HistoryLocationState>): UnregisterCallback;
createHref(location: LocationDescriptorObject<HistoryLocationState>): Href;
}
export type Location<S = LocationState> = {
pathname: Pathname;
search: Search;
state: S;
hash: Hash;
key?: LocationKey;
}
export interface LocationDescriptorObject<S = LocationState> {
pathname?: Pathname;
search?: Search;
state?: S;
hash?: Hash;
key?: LocationKey;
}
export namespace History {
export type LocationDescriptor<S = LocationState> = Path | LocationDescriptorObject<S>;
export type LocationKey = string;
export type LocationListener<S = LocationState> = (
location: Location<S>,
action: Action,
) => void;
// The value type here is a "poor man's `unknown`". When these types support TypeScript
// 3.0+, we can replace this with `unknown`.
type PoorMansUnknown = {} | null | undefined;
export type LocationState = PoorMansUnknown;
export type Path = string;
export type Pathname = string;
export type Search = string;
export type TransitionHook<S = LocationState> = (
location: Location<S>,
callback: (result: any) => void,
) => any;
export type TransitionPromptHook<S = LocationState> = (
location: Location<S>,
action: Action,
) => string | false | void;
export type Hash = string;
export type Href = string;
}
export type LocationDescriptor<S = LocationState> = History.LocationDescriptor<S>;
export type LocationKey = History.LocationKey;
export type LocationListener<S = LocationState> = History.LocationListener<S>;
export type LocationState = History.LocationState;
export type Path = History.Path;
export type Pathname = History.Pathname;
export type Search = History.Search;
export type TransitionHook<S = LocationState> = History.TransitionHook<S>;
export type TransitionPromptHook<
S = LocationState
> = History.TransitionPromptHook<S>;
export type Hash = History.Hash;
export type Href = History.Href;
}
/**
* react-router
*/
// new in v6
export function useRoutes(routeArray: RouteProps[]): Routes
export function useSearchParams(): URLSearchParams
// used to be SwitchProps
export interface RoutesProps {
children?: React.ReactNode;
location?: H.Location;
}
// used to be Switch
export class Routes extends React.Component<RoutesProps, any> {}
export interface RouteComponentProps<
Params extends { [K in keyof Params]?: string } = {},
C extends StaticContext = StaticContext,
S = H.LocationState
> {
history: H.History<S>;
location: H.Location<S>;
match: match<Params>;
staticContext?: C;
}
export interface match<Params extends { [K in keyof Params]?: string } = {}> {
params: Params;
isExact: boolean;
path: string;
url: string;
}
export interface RouteProps {
location?: H.Location;
element?: React.ReactNode; // SWYX: shortcut; have to do see if we can do better
render?: (props: RouteComponentProps<any>) => React.ReactNode;
children?:
// | ((props: RouteChildrenProps<any>) => React.ReactNode) // SWYX: i think this is not needed anymore?
| RouteProps[] // new in v6
| React.ReactNode;
path?: string | string[];
sensitive?: boolean;
// // no more in v6
// component?:
// | React.ComponentType<RouteComponentProps<any>>
// | React.ComponentType<any>;
// exact?: boolean;
// strict?: boolean;
}
export class Route<T extends RouteProps = RouteProps> extends React.Component<
T,
any
> {}
}
@swyxio
Copy link
Author

swyxio commented Feb 29, 2020

by the time you see this in future, RRv6 may already have types in it. please make sure to check remix-run/react-router#6955

if you have stuff to add, please comment here and i will update the gist that everyone can benefit

@swyxio
Copy link
Author

swyxio commented Feb 29, 2020

@tomasztunik
Copy link

tomasztunik commented Mar 2, 2020

Thanks for putting it down, small contribution to clear up the TODO note next to Outlet - you can overwrite the props property on the extended Component to prevent user from setting custom props or passing down children

  export class Outlet extends React.Component {
    props: {};
  }

@swyxio
Copy link
Author

swyxio commented Mar 5, 2020

@tomasztunik thanks, updated!

@checkmatez
Copy link

Thanks for putting it together. I believe useParams and useLocation hooks are still in v6. I just tried with 6.0.0-alpha.2 and they worked.

@vhenzl
Copy link

vhenzl commented Mar 6, 2020

Thanks for this! It seems to be missing Redirect component, it could be something like:

  export interface RedirectProps {
    from?: string;
    to: H.LocationDescriptor;
    children?: never;
  }
  export class Redirect extends React.Component<RedirectProps, any> {}

@swyxio
Copy link
Author

swyxio commented Mar 25, 2020

@vhenzl thanks i will copy it in

@checkmatez sure but i didnt use them (and i dont think there are docs?) anyway, i'll update.

@itswillta
Copy link

According to https://github.com/ReactTraining/react-router/blob/dev/packages/react-router/index.js#L392, I think it useNavigate's params should be:

  export function useNavigate(): (to: string, options?: Omit<NavigateProps<T>, 'to'>) => void;

Copy link

ghost commented Mar 31, 2020

can check how to achieve the following:
<button onClick={() => navigate(-1)}>Go back</button>

Thanks alot!

@swyxio
Copy link
Author

swyxio commented Mar 31, 2020

thanks @huy-ta, updated. mystical-ryan, you need to get navigate from useNavigate. unfortunately i wont be providing support for react router here - the focus is solely sharing types for super early adopters

Copy link

ghost commented Mar 31, 2020

Oh. apologies for the unclear question. according to the documentation, I can do navigate(-1), but I can't because typescript is complaining that -1 is not assignable to parameter of type 'string'. how do I update the .d.ts file that you have provided to overcome that? I am still new to typescript.

Thanks in adv

@swyxio
Copy link
Author

swyxio commented Mar 31, 2020

ah i see. i didnt know that :) try

  type navigateFn = (to: string | number, args?: { replace?: boolean, state?: object }) => void
  export function useNavigate(): (to: string, options?: Omit<NavigateProps<T>, 'to'>) => navigateFn;

i have updated the gist accordingly

Copy link

ghost commented Mar 31, 2020

thanks! think also need to include string | number in the following places:

export function useNavigate(): (to: string | number, options?: Omit<NavigateProps<T>, 'to'>) => navigateFn;
type NavigateProps<T> = {
    to: string | number,
    replace?: boolean,
    state?: T
  }

@cdaz5
Copy link

cdaz5 commented Apr 4, 2020

according to the new major changes here https://github.com/ReactTraining/react-router/releases/tag/v6.0.0-alpha.3 .. redirect was removed FYI

@swyxio
Copy link
Author

swyxio commented Apr 4, 2020

thanks @cdaz5 i removed it (i backed up v6.0.0-alpha2 typings here) and also added export function useSearchParams(): URLSearchParams

@gitpash
Copy link

gitpash commented Apr 9, 2020

thanks! think also need to include string | number in the following places:

export function useNavigate(): (to: string | number, options?: Omit<NavigateProps<T>, 'to'>) => navigateFn;
type NavigateProps<T> = {
    to: string | number,
    replace?: boolean,
    state?: T
  }

right in order to support navigate(-1) instead of goBack()
remix-run/react-router#7159 (comment)

@swyxio
Copy link
Author

swyxio commented Apr 9, 2020

ah thanks @gitpash

@gitpash
Copy link

gitpash commented Apr 10, 2020

export function useNavigate(T = object): (to: string | number, options?: Omit<NavigateProps<T>, 'to'>) => navigateFn; do you mean T = Object?

@swyxio
Copy link
Author

swyxio commented Apr 10, 2020

tbh i havent really tested that code (since my project doesnt need it) - can we do better than Object? what should we really be putting there?

@gitpash
Copy link

gitpash commented Apr 11, 2020

tbh i havent really tested that code (since my project doesnt need it) - can we do better than Object? what should we really be putting there?

Was looking at it more closely

export function useNavigate(T = object): (to: string | number, options?: Omit<NavigateProps<T>, 'to'>) => navigateFn;
  type NavigateOptions<T> = Omit<NavigateProps<T>, 'to'>
  type NavigateProps<T> = {
    to: string | number,
    replace?: boolean,
    state?: T
  }

not sure why passing T = Object as a prop to useNavigate (I'm not familiar with such pattern)
in my perspective main reason is to explicitly describe the shape of state prop in NavigateProps, if so

export function useNavigate<T>(): (to: string | number, options?: Omit<NavigateProps<T>, 'to'>) => navigateFn; - should be enough
but for more accuracy in order to define object like shape (to prevent smth like useNavigate<string>) we can use Metaobject

interface MetadataObj {
    [key: string]: any;
  }
  export function useNavigate<T extends MetadataObj>(): (
    to: string | number,
    options?: Omit<NavigateProps<T>, 'to'>
  ) => navigateFn;

this seems to be ok in my project

@swyxio
Copy link
Author

swyxio commented Apr 11, 2020

not sure why passing T = Object as a prop to useNavigate (I'm not familiar with such pattern)

that was a typo. I meant to write useNavigate<T = Object> instead - basically saying i declare a generic type T, and if the user doesnt give me anything, i'll just give it a default of Object.

ok i hear you - thats good enough for me to work with. i'll fix it up, cheers

@gitpash
Copy link

gitpash commented Apr 11, 2020

thank you! really appreciate your work here and in other projects 👍 I'm happy if I can help in any way

@swyxio
Copy link
Author

swyxio commented Apr 19, 2020

there is finally an attempt to update types on definitelytyped - see @murbanowicz's work here DefinitelyTyped/DefinitelyTyped#44015

i dont intend to maintain typings here much longer as i dont currently actively use RR

@murbanowicz
Copy link

I will appreciate any help with moving it forward! @gitpash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment