Можно ли вывести реквизиты первого дочернего элемента и применить их в TypeScript? Я могу подобрать его близко, но с дженериками он начинает давать сбой, и я не могу определить тип.
Я пытаюсь передать реквизиты компонента из оболочки первому дочернему компоненту с безопасностью типов. Если свойство не существует на объекте, TS должен завершиться неудачей, в противном случае он пройдет.
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>
</>
);
};
Вот почти рабочая TS Playground.
Вам просто нужно добавить общее ограничение к WrapperProps<C>
— то же самое, что есть у ComponentPropsWithoutRef<C>
— то есть ElementType
.
interface WrapperProps<C extends React.ElementType> {
children: ReactNode;
firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}
C
так, чтобы он соответствовал типу утилиты ComponentProps
, например. с ElementType
, как предложено в ответе СлавыСоболева:interface WrapperProps<C extends React.ElementType> {
children: ReactNode;
firstChildProps: Partial<React.ComponentPropsWithoutRef<C>>;
}
Wrapper
универсальным, но вам придется удалить React.FC
, что не работает с другими дженериками:const Wrapper = <C extends React.ElementType>({ children, firstChildProps }: WrapperProps<C>) => {
// etc.
}
Затем вы можете использовать его для различного контента:
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>
Какая версия IDE и TS? TS <= 5.0.4, похоже, не поддерживает синтаксис <Wrapper<typeof Button> >
tsplay.dev/mbrP4m
Ах, у меня это работает. У меня случайно был тип объединения firstChildProps: Partial<React.ComponentPropsWithoutRef<C>> | React.ReactNode
, который переопределил ошибку.
Это фантастика! Именно то, что я искал. Я вижу ошибки, возникающие на игровой площадке, но не вижу никаких ошибок в моей IDE (появляются другие ошибки TS). Кажется, что он не читает
<typeof Button>
в строках JSX. Есть идеи?