I'm using the types from @types/react-router-dom
version 4.3.1
which imports types from @types/react-router
and @types/history
.
In my application I always have two properties in the location state and I want them to automatically be pulled from the RouteComponentProps
interface, without having to pass a custom LocationState
type variable to the interface every time.
The definition of the RouteComponentProps
interface is in react-router
and it goes as follows:
import * as H from 'history';
export interface RouteComponentProps<Params extends { [K in keyof Params]?: string } = {}, C extends StaticContext = StaticContext, S = H.LocationState> {
history: H.History;
location: H.Location<S>;
match: match<Params>;
staticContext?: C;
}
The definitions of the referenced interfaces/types from history
are:
export interface Location<S = LocationState> {
pathname: Pathname;
search: Search;
state: S;
hash: Hash;
key?: LocationKey;
}
export type LocationState = History.LocationState;
export namespace History {
export type LocationState = any;
}
What I want is to expand the Location interface's state property type definition to include a custom interface (i.e. include the properties that are always available). So something like state: S & { A: string; B: string; }
.
I have tried redeclaring the RouteComponentProps
interface inside a module declaration for react-router
but everything I try results in All declarations of 'RouteComponentProps' must have identical type parameters.ts(2428)
or Subsequent property declarations must have the same type. Property 'location' must be of type 'Location<S>', but here has type 'any'.ts(2717)
.
I have also tried redeclaring the Location
interface inside the history module declaration with the same result. After which I've tried redeclaring the LocationState
type both inside and outside the History
namespace but that always results in Duplicate identifier 'LocationState'.ts(2300)
.
Is there anything I can do to get the desired behaviour other than writing a custom interface for RouteComponentProps
with a different name? I want to be able to import that interface in the project, extend a component's props with it and have properties' A
and B
types accessible from props.location.state
but also to be able to treat any other properties as any
, without passing a type variable definition every time.
So basically, what you are trying to achieve is called module augmentation
.
Since it is not possible to change the type of an existing property (for obvious reason, it would break everything that already depends on that), the only way is to augment the Location with a new property that would hold your
custom properties.
// global.d.ts
import { State, Key, Path } from "history"
declare module 'history' {
export interface Location<S extends State = State> extends Path {
state: S;
customState: { A: string; B: string; }
key: Key;
}
}
So, where you are adding those properties, if you want the Location
to be
properly typed, you have no choice than using a custom property.
The other solution you know it, it is to create a new interface that you will use everywhere.
With the module augmentation at least, even if you need a new property, you do not have to re-type everything everywhere.
Hope that this little explanation will help you out.
Cheers,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With