Home > database >  Is there a way to use generics to type a complex object array that accurately compares a React compo
Is there a way to use generics to type a complex object array that accurately compares a React compo

Time:07-19

My team has created typescript types definitions for an API that we're using, so we aren't able to touch the source code, just the definitions.

We have a function called add that essentially adds one or more react components to the main program. The props property of the object should depend on the component type listed on the component property.

It looks like this:

add(arg1, arg2, {
//some other args
children: [{
  id: string1,
  //BackButton can be a functional or class-based component
  component: BackButton,
  //BackButtonProps will be defined within BackButton
  props: BackButtonProps
},
  {
  id: string2,
  //SkipButton can be a functional or class-based component
  component: SkipButton,
  //SkipButtonProps will be defined within SkipButton
  props: SkipButtonProps
}
]
});

For reference, there is an alternate (overloaded) version of the function that only adds one component instead of multiple that I've been able to figure out the typescript for. It looks like this:

add<T>(
    id: string,
    component: ComponentType<T>,
    props: T
  ): void;

My question is this -- since the number of children components is unknown, how can I use a generic on the add function so that all the children components are correctly typed (the props and component properties match up)?

CodePudding user response:

A generic signature of add would look like this.

declare function add<T extends any[]>(arg1: any, arg2: any, arg3: {
    children: [...{ [K in keyof T]: {
      id: string,
      component: T[K]
      props: React.ComponentProps<T[K]>     
    }}]
}): void

The generic type T would store a tuple where each element is a react component. For each element with the index K of the passed array, the component type would be used as the type in T[K] and the props type would have to be React.ComponentProps<T[K]>.

Let's see this in action:

const BackButton = (props: {a: string}) => {
    return <div></div>
}

const SkipButton = (props: {b: string}) => {
    return <div></div>
}

add(0, 0, {
    children: [
        {
            id: "1",
            component: BackButton,
            props: { a: "123" }
        },
        {
            id: "2",
            component: SkipButton,
            props: { b: "123" }
        },
        {
            id: "3",
            component: SkipButton,
            props: { a: "123" } // Error: Type '{ a: string; }' is not assignable to type '{ b: string; }'
        }
    ]
})

Playground

  • Related