简体   繁体   中英

Typescript generic type for nested object properties

I want to achieve something like this so that the type of the Component can be inferred and so the props can be automatically safely typed.

type ComponentsType<T extends React.FC<any>> {
  [key: string]: {
    Component: T;
    props?: React.ComponentProps<T>
  };
}

const components: ComponentsType = {
   RedButton: {
      Component: Button,
      props: { // <- this should be automatically typed to take props of Button
        color: 'red'
      }
   },
   BlueButton: {
      Component: Button,
      props: { // <- this should be automatically typed to take props of Button
         color: 'blue'
      }
   },
   FullWidthCard: {
      Component: Card,
      props: { // <- this should be automatically typed to take props of Card
         variant: 'full-width'
      }
   },
}

But the above typing won't work because the generic part is on the root of the object AKA components , not on each property AKA RedButton or FullWidthCard .

So is there a way for typescript for run through them one by one and infer the Component one by one?

Thanks

In order to achieve expected behavior, you need to infer each component and props from function arguments. I mean, you need to create an extra function.

import React, { FC, ComponentProps } from 'react'


const Button: FC<{ color: string }> = (props) => null
const Card: FC<{ variant: string }> = (props) => null


type WithProps<T> =
    T extends Record<infer Key, infer _> ? {
        [P in Key]: T[Key] extends { Component: infer Comp, props: infer PassedProps }
        ? Comp extends React.JSXElementConstructor<infer Prps>
        ? { Component: Comp, props: ComponentProps<Comp> & Record<Exclude<keyof PassedProps, keyof Prps>, never> }
        : never
        : never
    } : never


const components = <
    Comp extends React.JSXElementConstructor<any>,
    Item extends { Component: Comp },
    Components extends Record<string, Item>>(dictionary: Components & WithProps<Components>) => {}

Playground

Comp - infered component

Item - infered dictionary item, component&props object

Components - infered whole argument.

WithProps - iterates through each object of passed argument and checks whether props obhect is assignable to component props. If yes - return props, otherwise negate all invalid props to never .

components({
    RedButton: {
        Component: Button,
        props: {
            color: 'red'
        }
    },
    BlueButton: {
        Component: Button,
        props: {
            extra_prop:42, // DRAWBACK, no error
            color: 'blue'
        }
    },
    FullWidthCard: {
        Component: Card,
        props: {
            variant_invalid_prop: 'full-width' // expected error
        }
    },
})

But there is a drawback, this function will accept extra props, like extra_prop .

PS I hope somebody will come up with better solution where extra props will be invalid also

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM