Alister
Alister

Reputation: 28329

Is it possible in React Typescript to use a component prop interface with another prop?

Basically, I want line injectComponentProps: object to be tied dynamically to whatever the injectComponent's prop interface is. In this case, it would be injectComponentProps: InjectedComponentProps, but I want it to be dynamic.

I.e Once prop injectComponent is set in , injectComponentProps is then defined to whatever injectComponent's components props are.

Is this possible?

interface InjectedComponentProps {
  variant: string
  size: number
}

const InjectedComponent: React.FC<InjectedComponentProps> = (props) => {
  return <Text {...props}>hello</Text>
}

interface ComponentProps {
  injectComponent: React.ReactType
  injectComponentProps: object
}

const Component: React.FC<ComponentProps> = (props) => {
  const InjectedComponent = props.injectComponent
  return (
    <>
      <InjectedComponent {...props.injectComponentProps}/>
    </>
  )
}


const Main: React.FC = () => {
  return (
    <Component
      injectComponent={InjectedComponent}
      injectComponentProps={{ variant: 'footnote', size: 14 }}
    />
  )
}

Upvotes: 3

Views: 1474

Answers (2)

zhirzh
zhirzh

Reputation: 3449

Instead of declaring the type simply as object, you should try modifying ComponentProps into a constrained generic component that infers the props of its type parameter.

The generic GetProps uses conditional type inference to infer props from a component, i.e., infer type parameter of a generic.

type GetProps<C extends React.ComponentType<any>> = C extends React.ComponentType<infer P> ? P : any

interface InjectedComponentProps {
  variant: string
  size: number
}

const InjectedComponent: React.FC<InjectedComponentProps> = (props) => {
  return <Text {...props}>hello</Text>
}

// make this a generic type
interface ComponentProps<C extends React.ComponentType<any>> {
  injectComponent: C
  injectComponentProps: GetProps<C>
}

// and use it here
const Component: React.FC<ComponentProps<typeof InjectedComponent>> = (props) => {
  const InjectedComponent = props.injectComponent
  return (
    <>
      <InjectedComponent {...props.injectComponentProps}/>
    </>
  )
}

const Main: React.FC = () => {
  return (
    <Component
      injectComponent={InjectedComponent}
      injectComponentProps={{ variant: 'footnote', size: 14 }}
    />
  )
}

Upvotes: 0

Alister
Alister

Reputation: 28329

interface InjectedComponentProps {
  variant: string
  size: number
}

const InjectedComponent: React.FC<InjectedComponentProps> = (props) => {
  return null
}

interface ComponentProps<T> {
  injectComponent: React.FC<T>
  injectComponentProps: T
}

const Component = <T extends {}>(props: ComponentProps<T>): JSX.Element => {
  const InjectedComponent = props.injectComponent
  return (
    <>
      <InjectedComponent {...props.injectComponentProps}/>
    </>
  )
}


const Main: React.FC = () => {
  return (
    <Component
      injectComponent={InjectedComponent}
      injectComponentProps={{ variant: 'footnote', size: 14 }}
    />
  )
}

Upvotes: 3

Related Questions