diff --git a/src/Redirect.tsx b/src/Redirect.tsx index c7b33507..5e694f7f 100644 --- a/src/Redirect.tsx +++ b/src/Redirect.tsx @@ -1,13 +1,19 @@ +/* eslint-disable max-classes-per-file */ +import React from 'react'; + import RedirectException from './RedirectException'; -import { Match, RedirectOptions } from './typeUtils'; +import { LocationDescriptor, Match, RedirectOptions } from './typeUtils'; + +class Redirect implements RedirectOptions { + path?: string; + + to: string | ((match: Match) => LocationDescriptor); + + status?: number; -class Redirect { constructor({ from, to, status }: RedirectOptions) { - // @ts-ignore this.path = from; - // @ts-ignore this.to = to; - // @ts-ignore this.status = status; } @@ -32,4 +38,10 @@ if (__DEV__) { (Redirect.prototype as any).isReactComponent = {}; } -export default Redirect; +// This actually doesn't extend a React.Component, but we need consumer to think that it does +declare class RedirectType extends React.Component { + // @ts-ignore + constructor(config: RedirectOptions); +} + +export default Redirect as unknown as RedirectType; diff --git a/src/additional.d.ts b/src/additional.d.ts index 272ecc99..e4b44686 100644 --- a/src/additional.d.ts +++ b/src/additional.d.ts @@ -5,7 +5,9 @@ declare global { declare let __DEV__: boolean; interface Window { __FOUND_HOT_RELOAD__: boolean; - __FOUND_REPLACE_ROUTE_CONFIG__: (routeConfig: RouteConfig) => void; + __FOUND_REPLACE_ROUTE_CONFIG__?: ( + routeConfig: RouteConfig, + ) => void | undefined; } } diff --git a/src/createBaseRouter.js b/src/createBaseRouter.tsx similarity index 81% rename from src/createBaseRouter.js rename to src/createBaseRouter.tsx index 4e5cea26..17e31034 100644 --- a/src/createBaseRouter.js +++ b/src/createBaseRouter.tsx @@ -1,5 +1,6 @@ import { dequal } from 'dequal'; import React from 'react'; +import { Store } from 'redux'; import warning from 'tiny-warning'; import ActionTypes from './ActionTypes'; @@ -8,6 +9,38 @@ import StaticContainer from './StaticContainer'; import createRender from './createRender'; import createStoreRouterObject from './createStoreRouterObject'; import resolveRenderArgs from './resolveRenderArgs'; +import { + ConnectedRouterProps, + CreateRenderOptions, + Match, + MatchBase, + RenderArgs, + Resolver, + Router, +} from './typeUtils'; + +interface CreateProps extends CreateRenderOptions { + render?: (args: RenderArgs) => React.ReactElement; +} + +interface BaseRouterProps extends ConnectedRouterProps { + store: Store; + match: MatchBase; + resolvedMatch: MatchBase; +} + +interface BaseRouterState { + isInitialRender: boolean; + match: MatchBase; + matchContext: any; + resolver: Resolver; + iteration: number; + routerContext: { + match: Match | null; + router: Router; + }; + element: React.ReactElement | null; +} export default function createBaseRouter({ renderPending, @@ -37,9 +70,19 @@ export default function createBaseRouter({ renderReady, renderError, }), -}) { - class BaseRouter extends React.Component { - constructor(props) { +}: CreateProps): React.ComponentClass { + class BaseRouter extends React.Component { + router: Router; + + lastIteration: number; + + pendingResolvedMatch: boolean; + + dispatchMatch: (pendingMatch: any) => void; + + mounted?: boolean; + + constructor(props: BaseRouterProps) { super(props); const { store, match, matchContext, resolver, initialRenderArgs } = @@ -99,7 +142,10 @@ export default function createBaseRouter({ } } - static getDerivedStateFromProps({ match, resolver, matchContext }, state) { + static getDerivedStateFromProps( + { match, resolver, matchContext }: BaseRouterProps, + state: BaseRouterState, + ) { if (state.isInitialRender) { return { isInitialRender: false }; } @@ -148,7 +194,8 @@ export default function createBaseRouter({ try { for await (const renderArgs of resolveRenderArgs( this.router, - this.props, + // TODO: this should be removed once resolveRenderArgs is in TS + this.props as any, )) { // Don't do anything if we're resolving an outdated match. if (!this.mounted || this.lastIteration !== pendingIteration) { @@ -178,7 +225,7 @@ export default function createBaseRouter({ this.dispatchMatch(pendingMatch); } } - } catch (e) { + } catch (e: any) { if (!this.mounted || this.lastIteration !== pendingIteration) { return; } diff --git a/src/createConnectedRouter.js b/src/createConnectedRouter.tsx similarity index 59% rename from src/createConnectedRouter.js rename to src/createConnectedRouter.tsx index ecbb210b..81134fed 100644 --- a/src/createConnectedRouter.js +++ b/src/createConnectedRouter.tsx @@ -1,20 +1,27 @@ import React from 'react'; import { shallowEqual, useSelector, useStore } from 'react-redux'; +import { Store } from 'redux'; import createBaseRouter from './createBaseRouter'; +import { + ConnectedRouterOptions, + ConnectedRouterProps, + ConnectedRouter as ConnectedRouterType, + FoundState, +} from './typeUtils'; export default function createConnectedRouter({ - getFound = ({ found }) => found, + getFound = ({ found }: any) => found as FoundState, ...options -}) { +}: ConnectedRouterOptions): ConnectedRouterType { const Router = createBaseRouter(options); - const getFoundState = (state) => { + const getFoundState = (state: Store) => { const { match, resolvedMatch } = getFound(state); return { match, resolvedMatch }; }; - function ConnectedRouter(props) { + function ConnectedRouter(props: ConnectedRouterProps) { const store = useStore(); const foundState = useSelector(getFoundState, shallowEqual); diff --git a/src/createFarceRouter.tsx b/src/createFarceRouter.tsx index db83437d..ed3e68f0 100644 --- a/src/createFarceRouter.tsx +++ b/src/createFarceRouter.tsx @@ -16,7 +16,6 @@ export default function createFarceRouter({ getFound = ({ found }: any) => found as FoundState, ...options }: FarceRouterOptions): FarceRouter { - // @ts-ignore TODO: take care of it once createBaseRouter is in TS const Router = createBaseRouter(options); const store =