javascript - Udled React-barnets props i TypeScript-interface
Er det muligt at udlede props for det første børneelement og håndhæve dem i TypeScript? Jeg kan komme tæt på, men det begynder at fejle med generics, og jeg har ikke været i stand til at udlede typen.
Jeg prøver at videregive komponentprops fra en wrapper til det første børnekomponent med typesikkerhed. Når proppen ikke eksisterer på objektet, skal TS fejle, ellers passere.
import React, {
Children,
isValidElement,
cloneElement,
ReactNode,
} from 'react';
interface WrapperProps<C> {
children: ReactNode;
// how can I make typeof Button generic/inferred from the code?
// firstChildProps: Partial<React.ComponentProps<typeof Button>>; // works with <C> removed
firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}
const Wrapper: React.FC<WrapperProps<typeof Button>> = ({
children,
firstChildProps,
}) => {
const firstChild = Children.toArray(children)[0];
if (isValidElement(firstChild)) {
// Clone the first child element and pass the firstChildProps to it
return cloneElement(firstChild, { ...firstChildProps });
} else {
return <>{children}</>;
}
};
interface ButtonProps {
disabled: boolean;
children: ReactNode;
}
const Button: React.FC<ButtonProps> = ({ disabled, children }) => {
return <button disabled={disabled}>{children}</button>;
};
const Example = () => {
return (
<>
{/* Passes type check because disabled exists on Button */}
<Wrapper firstChildProps={{ disabled: false }}>
<Button disabled={true}>Ok</Button>
</Wrapper>
{/* Fails type check because cheese does not exist on Button */}
<Wrapper firstChildProps={{ cheese: true }}>
<Button disabled={true}>Ok</Button>
</Wrapper>
</>
);
};
Her er en næsten fungerende TS Playground.
Svar #1
- Du skal begrænse den generiske typeparameter
C, så den passer tilComponentPropshjælpefunktionen, f.eks. medElementType, som foreslået i SlavaSobolev's svar:
interface WrapperProps<C extends React.ElementType> {
children: ReactNode;
firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}
- Du kan også gøre
Wrapper-komponenten generisk, men du ville være nødt til at droppeReact.FC, som ikke fungerer med yderligere generics:
const Wrapper = <C extends React.ElementType>({ children, firstChildProps }: WrapperProps<C>) => {
// etc.
}
Så kan du bruge det til forskelligt indhold:
interface OtherProps {
foo: "bar";
}
const Other: React.FC<OtherProps> = () => null;
{/* Fails type check because cheese does not exist on Button */}
<Wrapper<typeof Button> firstChildProps={{ cheese: true }}>
<Button disabled={true}>Ok</Button>
</Wrapper>
{/* Error: Type 'number' is not assignable to type '"bar"'. */}
<Wrapper<typeof Other> firstChildProps={{ foo: 0 }}>
<Other foo="bar" />
</Wrapper>
Legeplads link
Svar #2
Du skal bare tilføje en generisk begrænsning til WrapperProps<C> - den samme, som ComponentPropsWithoutRef<C> har - nemlig ElementType.
interface WrapperProps<C extends React.ElementType> {
children: ReactNode;
firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}
