Home > Software design >  extracting the generic type of other props in interface
extracting the generic type of other props in interface

Time:11-15

How can I achieve something like this?

interface Props {
   result: <type of function with a lot of generic>
   data: <type of one of the generics in the function above>
}

I've found a question that sounds similar here, but I'm such a newbie and not sure if this applies to my case.

The background:

I'm using react-query, and want to make a wrapper component that takes a react-query result object and a component, then shows spinner or the given component based on the query state (isLoading, isSuccess etc), whatever the query response data type is.

import React from 'react'
import {useQuery} from 'react-query'

interface WrapperProps {
   result: <type of react query result>;
   render: (data: <type of query data>) => React.ReactNode;
}

const Wrapper: React.FC<WrapperProps> = ({result, render}) => {
  if (result.isLoading) {
     return <div>spinner</div>
  }

  if (result.isSuccess) {
    return render(result.data)
  }

  return <div>otherwise</div>
}


const App: React.FC = () => { 
  const responseString = useQuery(
    ['string']
    async () => Promise.resolve("string")
  )

  const responseNumber = useQuery(
    ['number']
    async () => Promise.resolve(3)
  )

  return (
    <div>
    <Wrapper 
      result={responseString}
      render={(data) => (<div>successfully loaded string {data}</div>)} // <= want the data to be type string
    />
    <Wrapper 
      result={responseInt}
      render={(data) => (<div>successfully loaded int {data}</div>)} // <= want the data to be type number
    />
    </div>

  )
}

*1 the type of useQuery is something like this

CodePudding user response:

You were close. You need to update Wrapper component a bit.

First of all, you need to get rid FC. Instead you need to add extra generic type to infer query result.

Consider this example:

import React from 'react'

import { useQuery, UseQueryResult } from 'react-query'

interface WrapperProps<T> {
  result: UseQueryResult<T, unknown>
  render: (data: T) => JSX.Element;
}

const Wrapper = <T,>({ result, render }: WrapperProps<T>) => {
  if (result.isLoading) {
    return <div>spinner</div>
  }

  if (result.isSuccess) {
    return render(result.data)
  }

  return <div>otherwise</div>
}


const App: React.FC = () => {
  const responseString = useQuery(
    ['string'],
    async () => Promise.resolve("string")
  )

  const responseNumber = useQuery(
    ['number'],
    async () => Promise.resolve(3)
  )

  return (
    <div>
      <Wrapper
        result={responseString}
        render={(data /** string */) => (<div>successfully loaded string {data}</div>)}
      />
      <Wrapper
        result={responseNumber}
        render={(data /** number */) => (<div>successfully loaded int {data}</div>)}
      />
    </div>

  )
}

Playground

Please keep in mind that it is impossible to infer component props with extra generic and FC explicit type.

Please be aware that TS since 2.9 supports explicit generics for react components

  • Related