reactjs - Hvordan man undgår at gendeklarere prop-typen i React-konstruktøren ved brug af Typescript
Lad os sige, at jeg har en klasse som denne:
class PeopleByTag extends React.Component<RouteComponentProps<{ tag: string }>
Jeg har brug for at gøre noget i min constructor, i dette eksempel hente data, men for at gøre det skal jeg erklære en props-parameter, men hvis jeg ikke angiver typen, vil den blive til any:
constructor(props) {
super(props); // props is any
this.loadData();
}
På den anden side, hvis jeg erklærer typen igen, bliver koden meget grim:
constructor(props: Readonly<{
children?: React.ReactNode;
}> & Readonly<RouteComponentProps<{
tag: string;
}, StaticContext, any>>) {
super(props);
this.loadData();
}
Er der en måde automatisk at udlede props-typen fra klasseudvidelsen, samtidig med at man kan skrive en constructor?
Jeg vil heller ikke bruge de forældede livscyklus-kroge (dvs. ComponentWillMount).
Svar #1
Normalt bør constructor selv ikke blive 'meget grim', bare fordi typer kan defineres separat som type eller interface, hvis parametertyperne er omstændelige.
Konstruktørens props-parameter kan ikke udledes, fordi React.Component<RouteComponentProps<{ tag: string }>>-generisk parameter henviser til forældresklassen, React.Component, ikke den aktuelle klasse.
Som det kan ses i typedefinitioner, udleder dette korrekt type for forældre-konstruktøren, dvs. super.
Så dette
constructor(props) {
super(props);
}
er gyldig. this.props er stadig korrekt typet.
I tilfælde af at noImplicitAny compiler-indstilling bruges, er det:
constructor(props: any) {
super(props);
}
Brugen af props typet som any i konstruktøren kan resultere i typemæssige fejl:
constructor(props: any) {
super(props);
props.tag; // ok
props.nonexistentProp; // ok
}
Mens this.props er type-sikkert.
En klasse kan typificeres med generics for at opretholde korrekt props-type i konstruktøren, men dette kan betragtes som overkill:
export class PeopleByTag<P extends { tag: string }> extends React.Component<P> {
constructor(props: Readonly<P>) {
super(props); // props is any
props.tag; // ok
props.nonexistentProp; // not ok
props.children; // not ok, but we rarely ever need children in constructor
}
}
Det kan være gavnligt at forhindre brugen af props i konstruktøren ved at give en inkompatibel type til den:
constructor(props: never) {
super(props);
props.tag; // not ok
}
Hvis props-argumentet blev sendt til super, er this.props og props udskiftelige i JavaScript. De er ikke udskiftelige i TypeScript. this.props kan tilgås i konstruktøren for korrekt typede props.
Svar #2
Jeg vil anbefale at definere interfaces eller typealias for at reducere duplikeringen, som estus sagde. Bemærk dog, at typen af props-parameteren til konstruktøren bare kan være Readonly<RouteComponentProps<{ tag: string }>>, da TypeScript-kompilatoren automatisk vil tilføje children-egenskaben baseret på deklarationer i @types/react, og du kan stole på standardværdierne for det andet og tredje typeargument til RouteComponentProps, præcis som du gjorde i extends-klausulen. (Man kan argumentere for, at hvis parameteren til konstruktøren er det rigtige props-objekt, så bør dens type inkludere children-egenskaben, og at udelade children-egenskaben er forkert, men udeladelsen er almindelig praksis og findes endda i deklarationen af React. Komponent superkonstruktor. Jeg har også udeladt Readonly i ét projekt, men det ser ud til, at det måske ikke længere er anbefalelsesværdigt.) På det tidspunkt vil du måske beslutte, at typen er kort nok til, at det er i orden at gentage den, og at det ikke er værd at definere et interface eller typealias.
Forresten, her er det åbne forslag om, at metoder og konstruktørparametre som standard skal bruge typerne fra superklassen.
