reactjs - Fuzionarea declarațiilor în Typescript pentru a extinde sau suprascrie proprietatea de tip sau interfață a modulului
Folosesc tipurile din @types/react-router-dom versiunea 4.3.1 care importă tipuri din @types/react-router și @types/history.
În aplicația mea am întotdeauna două proprietăți în starea locației și vreau ca acestea să fie preluate automat din interfața RouteComponentProps, fără a fi nevoie să trec de fiecare dată o variabilă de tip personalizat LocationState la interfață.
Definiția interfeței RouteComponentProps se află în react-router și este următoarea:
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;
}
Definițiile interfețelor/tipurilor referențiate din history sunt:
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;
}
Ceea ce vreau este să extind definiția tipului proprietății state din interfața Location pentru a include o interfață personalizată (adică să includă proprietățile care sunt întotdeauna disponibile). Deci ceva de genul state: S & { A: string; B: string; }.
Am încercat să redefinesc interfața RouteComponentProps într-o declarație de modul pentru react-router, dar tot ce încerc duce la All declarations of 'RouteComponentProps' must have identical type parameters.ts(2428) sau Subsequent property declarations must have the same type. Property 'location' must be of type 'Location<S>', but here has type 'any'.ts(2717).
Am încercat, de asemenea, să redeclarez interfața Location în interiorul declarației modulului history cu același rezultat. După aceea am încercat să redeclarez tipul LocationState atât în interiorul, cât și în afara namespace-ului History, dar acest lucru duce întotdeauna la Duplicate identifier 'LocationState'.ts(2300).
Există ceva ce pot face pentru a obține comportamentul dorit în afară de a scrie o interfață personalizată pentru RouteComponentProps cu un alt nume? Vreau să pot importa acea interfață în proiect, să extind proprietățile unui component cu ea și să am tipurile proprietăților A și B accesibile din props.location.state, dar și să pot trata orice alte proprietăți ca any, fără a trece o definiție de variabilă de tip de fiecare dată.
Răspuns #1
Deci practic, ceea ce încerci să realizezi se numește augmentarea modulului. Deoarece nu este posibil să schimbi tipul unei proprietăți existente (din motive evidente, ar strica tot ce depinde deja de aceasta), singura modalitate este să completezi obiectul Location cu o proprietate nouă care ar ține proprietățile tale personalizate.
// 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;
}
}
Deci, unde adaugi acele proprietăți, dacă vrei ca Location să fie tipizat corespunzător, nu ai altă opțiune decât să folosești o proprietate personalizată.
Cealaltă soluție, o știi, este să creezi o interfață nouă pe care o vei folosi peste tot.
Cu cel puțin augmentarea modulului, chiar dacă ai nevoie de o proprietate nouă, nu trebuie să re-scrii totul peste tot.
Sper că această mică explicație te va ajuta.
Noroc,
Răspunsul #2
Acesta s-ar putea să nu fie soluția pe care o cauți, dar de obicei am o interfață personalizată pentru locație (poate să o exporți dintr-un fișier .ts partajat în aplicația ta), apoi folosesc hook-ul useLocation cu acea interfață.
import React, { ReactElement } from "react";
import { useLocation } from "react-router-dom";
interface CustomLocationState {
A: string;
B: string;
}
export default ():ReactElement => {
const { state: {A, B} } = useLocation<CustomLocationState>();
return <div>{A} - {B}</div>
}
