reactjs - Vaadi tietyn tyyppistä funktionaalista komponenttia propina Reactissa TypeScriptin kanssa


Sano, että sinulla on komponenttipuu, kuten:

 function Parent(props: { componentProp: ReactElement }) {
   return (
     <>
       {props.componentProp}
     </>
   );
 }

 function ChildA(props: { message: string }) { return (<h1>{props.message}</h1>) }
 function ChildB(props: { message: string }) { return (<h2>{props.message}</h2>) }

 function App() {
   return (
     <Parent componentProp={<ChildA message="hi"/>}
   );
 }

Kuten kirjoitettuna, voit korvata ChildA ChildB:llä componentProp:ssa, eikä Typescript valittaisi.

Onko mahdollista rajoittaa componentProp-tyyppi siten, että siihen voisi antaa vain ChildA, ja ChildB-antaminen aiheuttaisi tyyppivirheen?

Vastaus #1

Minkä tahansa renderöidyn JSX:n tyyppi on aina sama -- en tiedä mitään tapaa erottaa niitä. Vaikka tekisit jotain tällaista, renderöidyn JSX:n tyyppi ei muutu funktion paluuarvon tyypin mukaan:

type Valid = ReactElement & { __valid: true }
function ChildA(props: { message: string }): Valid {
    const result = <h1>{props.message}</h1>
    return { ...result, __valid: true } // fails
}
const ca = <ChildA message="foo"/> // still ReactElement

Kuitenkin voit erottaa komponenttien funktiot itsessään ja välittää niitä propsina sen sijaan, että käyttäisit niiden renderöityjä versioita. Jos voimassa olevilla komponenteilla on erilaiset propsit, voit hyödyntää sitä, muuten voit lisätä joko turhan propin tai turhan palautettavan objektin ominaisuuden:

export default null
type Valid = (props: unknown) => ReactElement & { _valid: null }

// option 1:
const A = () => {
    const result = <></>
    return { ...result, _valid: null }
}

// option 2:
const B = (() => <></>) as unknown as Valid

const C = () => <></>

function Parent({ Child }: { Child: Valid }) {
    return <><Child /></>
}

function App() {
    return (
        <>
            <Parent Child={A} /> {/*passes*/}
            <Parent Child={B} /> {/*passes*/}
            <Parent Child={C} /> {/*fails*/}
        </>
    )
}