Skip to content

Instantly share code, notes, and snippets.

@swyxio
Last active October 4, 2022 00:36
Show Gist options
  • Select an option

  • Save swyxio/37a6a3d248c2d4031801f0d568904df8 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
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
Copy Markdown
Author

swyxio commented Feb 29, 2020

@tomasztunik
Copy link
Copy Markdown

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
Copy Markdown
Author

swyxio commented Mar 5, 2020

@tomasztunik thanks, updated!

@checkmatez
Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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
Copy Markdown

ghost commented Mar 31, 2020

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

Thanks alot!

@swyxio
Copy link
Copy Markdown
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
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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
Copy Markdown
Author

swyxio commented Apr 9, 2020

ah thanks @gitpash

@gitpash
Copy link
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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