2

I have got a generic union type for fetching data called RemoteData for which I tried to create a higher order component:

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);
      }
    }
  };
}

But these types do not work. Not sure if what I'm trying to do is even possible, anyway I'm getting

TS2322: Type '{ data: T; }' is not assignable to type 'K'.

When I try to assign remote.data to data of XComponent.

Here you find all of the code needed to reproduce the problem. Is there a way to fix these types?

1

You could simply do

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

  • But this limits the component which can be passed to this HOC. If I pass any component with more that one prop data. codesandbox.io/s/react-typescript-playground-qk3sp It cuts off other props. – Konrad Klimczak Jun 7 at 9:10
1

The parameter type for the HOC, XComponent, expects its properties to conform to some type K, which may contain some extra properties in addition to data.

But the props type for the HOC, IWithRemote<T>, has remote property which is declared to have only data when kind is Success, nothing else.

So, when you render XComponent with

<XComponent data={remote.data} />

Where are all the other properties in K that XComponent expects to receive for its props are supposed to come from?

Anyway, if everything that XComponent needs is coming together with data in remote when kind is Success, you can declare that it's indeed so - you just need to add that K parameter to RemoteData and IWithRemote, and declare Success union member as intersection type

   {
            kind: RemoteDataKind.Success;
      } & K

The complete types are

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>> {

Then when you render XComponent you need to make sure that everything that's in remote is passed to it, not just data:

            case RemoteDataKind.Success:
                return <XComponent {...remote} />;
  • It kind of worked... but it also takes all of the props of component passed to HOC from props to remote. You can see it here: codesandbox.io/s/react-typescript-playground-txk9f somethingElse, becomes a property of remote in Success state, instead of being a prop. – Konrad Klimczak Jun 7 at 10:48
  • "it also takes all of the props of component passed to HOC from props to remote" - I put them there because I don't know where else these properties can come from. Are any of them non-optional? Do you need any knowledge of the exact type of these properties inside the HOC? If the answer to these questions is "no", I don't think you even need K generic type parameter, you can simply use XComponent: React.ComponentType<IWithData<T>> – artem Jun 7 at 10:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.