2

Im having some trouble defining a generic for this specific scenarion.

I have multiple React components defined like this:

type ComponentFunction<T> = (props: T) => void;

interface FooProps {
  bar: string;
}

const Foo: ComponentFunction<FooProps> = ({ bar }) => {};

and I want to have an interface that would look something like this:

interface Component<T> {
  element: ComponentFunction<T>;
  props: T;
}

what I would like is to have an array of those components without having to define the generic for each and every one of them. It would just be automatically "detected" from the passed element, for example:

const components: Component<?>[] = [
  {
    element: Foo,
    props: {
      bar: "bar"
    }
  },
  ...
]

Is this even possible?

Edit 1:
Replaced React.FC with a more generic example ComponentFunction.

Solution 1:

const componentArray = <T extends any[]>(array: { [I in keyof T]: Component<T[I]> }) => array;

const components = componentArray([
  {
    element: Foo,
    props: {
      bar: "string"
    }
  },
  ...
]);

Solution 2:

type SomeComponent = <R>(cb: <T>(component: Component<T>) => R) => R;

const someComponent = <T,>(component: Component<T>): SomeComponent => cb => cb(component);

const components: SomeComponent[] = [
  someComponent({ element: Foo, props: { bar: "string" } }),
  ...
];

Thanks to @jcalz for providing the solutions.

  • Not really sadly. Maybe the example was too React specific. I will update it. The biggest issue I'm having with this is how to avoid defining the generic here `Component>[]` – lilBunnyRabbit Jun 14 '22 at 17:55
  • 1
    This is a canonical use case for *existentially quantified generics*, which TS doesn't have direct support for (but neither do most languages, so it's not a problem with TS per se). Your choices here are either to make everything generic and map over an array type, or to emulate existentials. [This playground link](https://tsplay.dev/wRGl1N) shows both approaches. – jcalz Jun 14 '22 at 18:12
  • 1
    See [this Q/A](https://stackoverflow.com/questions/65129070/defining-an-array-of-differing-generic-types-in-typescript) for more information. – jcalz Jun 14 '22 at 18:15
  • @jcalz Thank you! Visually it's not the nicest solution but sadly it's the only solution. For my case I prefer to make everything generic. Also very nice explanation on [this Q/A](https://stackoverflow.com/questions/65129070/defining-an-array-of-differing-generic-types-in-typescript)! – lilBunnyRabbit Jun 14 '22 at 18:31
  • @jcalz Interesting issue with both solutions. If I define `FooProps` as `interface FooProps { bar: "a" | "b" | "c" }` I get an error `Type '"string"' is not assignable to type '"a" | "b" | "c"'.` – lilBunnyRabbit Jun 14 '22 at 19:18
  • That has to do with type inference from string literal values and has nothing to do with heterogeneous arrays, so it doesn't seem to be in scope for this question. – jcalz Jun 14 '22 at 19:21

0 Answers0