2022-03-21

How to write a generic type that includes all props from one generic type that are not in another?

I'm trying to write a generic HOC that takes a component with props TProps and a type TEntry defining an object structure. I want the HOC to return a component which will call a hook to get an object instance of type TEntry from a particular source (in this case a CMS). This resulting component should only require the consumer to provide the props that are not filled in by that CMS data.

I thought that Exclude<TProps, TEntry> would work for this, but I'm still ending up with a component that requires all of TProps. Here's what I've tried (this is rather stripped down for simplicity):

const withCmsData = <
  TEntry,
  TProps,
>(
  Component: (props: TProps) => (JSX.Element | null)
) => {

  return (props: Exclude<TProps, TEntry>) => {

    const { entry } = useCmsEntries<TEntry>();

    return <Component {...entry} {...props} />;
  };
};

...and here's an example of how you'd use it to create a new component with fewer props:

type ArticlePageProps = {
  title: string;
  subTitle: string;
  date: string;
}
const ArticlePage = ({ title, subTitle, date }: ArticlePageProps) => {
  return (
    <>
      <h1>{title}</h1>
      <div>{subTitle}</div>
      <div>{date}</div>
    </>
  );
}

type ArticleFromCmsProps = {
  title: string;
  subTitle: string;
}
/*
This creates a new component that now only requires a data prop,
which is the one prop from ArticlePageProps that is
not provided by ArticleFromCmsProps:
*/
const MyPageWrapper = withCmsData<ArticlePageProps, ArticleFromCmsProps>(ArticlePage);

const render = () => {

  const data = new Date();
  <MyPageWrapper date={date} />
}

Is there a way to get the generic HOC I want? One that returns a component only requiring props not provided by a call to the CMS?



No comments:

Post a Comment