reactjs - Högre ordningens komponent med generiska inception-typer
Jag har en generell unionstyp för att hämta data som heter RemoteData och för den försökte jag skapa en högre ordningskomponent:
export interface IWithRemote<T> {
remote: RemoteData<T>;
}
export interface IWithData<T> {
data: T;
}
export function withRemoteData<T, K extends IWithData<T>>(XComponent: React.ComponentType<K>) {
return class extends React.Component<IWithRemote<T>> {
render() {
const { remote } = this.props;
switch (remote.kind) {
case RemoteDataKind.NotAsked:
return <div> nice not asked yet </div>;
case RemoteDataKind.Loading:
return <Spinner />;
case RemoteDataKind.Success:
return <XComponent data={remote.data} />
case RemoteDataKind.Failure:
return <div>daaaamn</div>;
default:
return assertNever(remote);
}
}
};
}
Men dessa typer fungerar inte. Jag är inte säker på om det jag försöker göra ens är möjligt, hur som helst får jag
TS2322: Typen '{ data: T; }' kan inte tilldelas typen 'K'.
När jag försöker tilldela remote.data till data i XComponent.
Här hittar du all kod som behövs för att återskapa problemet. Finns det ett sätt att fixa dessa typer?
Svar #1
Du skulle helt enkelt kunna göra
export function withRemoteData<T>(
XComponent: React.ComponentType<IWithData<T>>
) {
return class extends React.Component<IWithRemote<T>> {
render() {
const { remote } = this.props;
switch (remote.kind) {
case RemoteDataKind.NotAsked:
return <div> nice not asked yet </div>;
case RemoteDataKind.Loading:
return <div> spinnnnnner </div>;
case RemoteDataKind.Success:
return <XComponent data={remote.data} />;
case RemoteDataKind.Failure:
return <div>daaaamn</div>;
default:
return assertNever(remote);
}
}
};
}
https://codesandbox.io/s/react-typescript-playground-38bnw
Svar #2
Parametertypen för HOC, XComponent, förväntar sig att dess egenskaper överensstämmer med någon typ K, som kan innehålla några extra egenskaper utöver data.
Men props-typen för HOC:en, IWithRemote<T>, har remote-egenskapen som är deklarerad att endast ha data när kind är Success, inget annat.
Så, när du renderar XComponent med
<XComponent data={remote.data} />
Var ska alla andra egenskaper i K som XComponent förväntas ta emot för sina props komma ifrån?
Hur som helst, om allt som XComponent behöver kommer samman med data i remote när kind är Success, kan du deklarera att det faktiskt är så - du behöver bara lägga till den K-parametern till RemoteData och IWithRemote, och deklarera Success union-medlem som en snitt-typ
{
kind: RemoteDataKind.Success;
} & K
De fullständiga typerna är
type RemoteData<T, K extends IWithData<T>> =
| {
kind: RemoteDataKind.NotAsked;
}
| {
kind: RemoteDataKind.Loading;
}
| {
kind: RemoteDataKind.Success;
} & K
| {
kind: RemoteDataKind.Failure;
error: Error;
};
interface IWithRemote<T, K extends IWithData<T>> {
remote: RemoteData<T, K>;
}
interface IWithData<T> {
data: T;
}
export function withRemoteData<T, K extends IWithData<T>>(
XComponent: React.ComponentType<K>
) {
return class extends React.Component<IWithRemote<T, K>> {
När du sedan renderar XComponent behöver du se till att allt som finns i remote skickas till det, inte bara data:
case RemoteDataKind.Success:
return <XComponent {...remote} />;
