#reactjs #typescript #typescript-typings #react-typescript
#reactjs #машинописный текст #машинопись-типизации #реагировать-машинопись
Вопрос:
Я создаю приложение React с помощью TypeScript. У меня есть RequiresPermission
компонент, который на основе предиката должен отображать один или другой компонент и пересылать все реквизиты.
type Props = {
NotPermittedComponent: React.ComponentType;
PermittedComponent: React.ComponentType;
isPermitted: boolean;
};
const RequiresPermisson = ({
NotPermittedComponent,
PermittedComponent,
isPermitted,
...rest
}: Props) =>
isPermitted ? (
<PermittedComponent {...rest} />
) : (
<NotPermittedComponent {...rest} />
);
export default RequiresPermisson;
Когда я визуализирую компонент, TypeScript кричит о RequiresPermission
:
const PERMITTED_TEXT = 'permitted';
const NOT_PERMITTED_TEXT = 'not-permitted';
type TestPropsProps = {
text: string;
};
const NotPermittedTestComponent: React.FunctionComponent<TestPropsProps> = ({
text,
}) => (
<div>
<span>{NOT_PERMITTED_TEXT}</span>
{text}
</div>
);
const PermittedTestComponent: React.FunctionComponent<TestPropsProps> = ({
text,
}) => (
<div>
<span>{PERMITTED_TEXT}</span>
{text}
</div>
);
const createProps = ({
NotPermittedComponent = NotPermittedTestComponent,
PermittedComponent = PermittedTestComponent,
isPermitted = false,
text = 'foo',
} = {}) => ({
NotPermittedComponent,
PermittedComponent,
isPermitted,
text,
});
const props = createProps();
render(<RequiresPermission {...props} />);
говоря:
Type '{ NotPermittedComponent: FunctionComponent<TestPropsProps>; PermittedComponent: FunctionComponent<TestPropsProps>; isPermitted: boolean; text: string; }' is not assignable to type '{ NotPermittedComponent: ComponentType<{}>; PermittedComponent: ComponentType<{}>; isPermitted: boolean; }'.
Types of property 'NotPermittedComponent' are incompatible.
Type 'FunctionComponent<TestPropsProps>' is not assignable to type 'ComponentType<{}>'.
Type 'FunctionComponent<TestPropsProps>' is not assignable to type 'FunctionComponent<{}>'.
Types of parameters 'props' and 'props' are incompatible.
Type '{ children?: ReactNode; }' is not assignable to type 'PropsWithChildren<TestPropsProps>'.
Property 'text' is missing in type '{ children?: ReactNode; }' but required in type 'TestPropsProps'.
Я тоже пробовал Record<string, unknown>
as props
, но это тоже не сработало.
Как вы можете исправить это, чтобы либо передать реквизит, либо разрешить любой (не тип any
) реквизит, чтобы ...rest
параметр работал?
Комментарии:
1. «разрешить любые реквизиты» —
(props: any) => ...
?unknown
имеет больше смысла как возвращаемый тип, чем тип параметра.2. поскольку вы не принимаете никакого ответа, я бы предположил, что чего-то не хватает. Не могли бы вы дать какие-либо отзывы о том, какая часть вашего вопроса не получила ответа или почему она не соответствует вашему варианту использования?
Ответ №1:
Не уверен, что это самое элегантное решение, но я только что объединил все типы реквизитов. Это дает вам возможность проверки типа для всех реквизитов. Я должен переслать все реквизиты, потому что я не могу убедиться, что что-то подобное isPermitted
не требуется для подкомпонента.
interface Props <A, B>{
NotPermittedComponent: React.ComponentType<A>;
PermittedComponent: React.ComponentType<B>;
isPermitted: boolean;
}
const RequiresPermisson = <A, B>(props: Props<A, B> amp; A amp; B): JSX.Element => {
const {
NotPermittedComponent,
PermittedComponent,
isPermitted,
} = props
return isPermitted ? (
<PermittedComponent {...props} />
) : (
<NotPermittedComponent {...props} />
)
}
Комментарии:
1. A это потрясающе! Зачем вам нужны оба
<A, B>
, а не только<A>
? @ian2. A и B — это просто реквизиты PermittedComponent и NotPermittedComponent , поскольку было предложено, чтобы они были разными, нам нужны разные типы для них. A и B будут выведены из вставляемых вами компонентов, и все они будут необходимы / возможны при RequiersPermission. @Gabriel
3. Я принял ответ, потому что, по крайней мере, он компилируется, но технически есть еще одна проблема.
PermittedComponent
аNotPermittedComponent
теперь получают самих себя иisPermitted
передаются им в качестве реквизита, но это не их реквизит. У вас есть какое-либо исправление для этого?4. Я понимаю ваше беспокойство. Но даже с
...rest
помощью из вашего кода выPermittedComponent
также предоставите реквизитыNotPermittedComponent
. А что, если я напишуPermittedComponent
книгу, которая будетisPermitted
служить опорой? Я не уверен, что есть способ разделить все это, не выделяя реквизит во что-то вродеpermittedComponentProps
или передавая их как renderProps.
Ответ №2:
Вам нужны общие типы из typescript: https://www.typescriptlang.org/docs/handbook/generics.html
Короче говоря, это динамические типы, которые зависят от того, что вводится.
В приведенном ниже коде мы присваиваем всем реквизитам, которые передаются, тип T, и сообщаем typescript, что реквизитами являются: обязательные реквизиты NotPermittedComponent
PermittedComponent
isPermitted
вместе со всеми rest
, которые в этом случае становятся типом T.
type Props = {
NotPermittedComponent: React.ComponentType;
PermittedComponent: React.ComponentType;
isPermitted: boolean;
};
const RequiresPermisson = <T extends Record<string, unknown>>({
NotPermittedComponent,
PermittedComponent,
isPermitted,
...rest
}: Props amp; T) =>
isPermitted ? (
<PermittedComponent {...rest} />
) : (
<NotPermittedComponent {...rest} />
);
export default RequiresPermisson;
Комментарии:
1. Спасибо за вашу помощь. Я попробовал ваш код, он выдает ту же ошибку:
Types of property 'NotPermittedComponent' are incompatible. Type 'FunctionComponent<TestProps>' is not assignable to type 'ComponentType<{}>'. Type 'FunctionComponent<TestProps>' is not assignable to type 'FunctionComponent<{}>'. Types of parameters 'props' and 'props' are incompatible. Type '{ children?: ReactNode; }' is not assignable to type 'PropsWithChildren<TestProps>'. Property 'text' is missing in type '{ children?: ReactNode; }' but required in type 'TestProps'.
2. Боже, это сложная штука. Пытался создать способ передачи любого компонента как
PermittedComponent
иNotPermittedComponent
, и требовать передачи всех и только реквизитов дляRequiresPermisson
компонента. Искал по всему Интернету и, похоже, не является способом AFAIK. Проблема заключается в динамических реквизитахPermittedComponent
иNotPermittedComponent
, всегда ли они содержат только {text: string} ? В этом случае я был бы очень прост.3. Я знаю правильно 😕 Спасибо, что использовали свои выходные, чтобы помочь мне, кстати, очень ценю это 🙏 Я также потратил на это часы и не смог найти способ заставить это работать. Нет, PermittedComponent и NotPermittedComponent могут даже содержать оба разных реквизита (то есть один
{ myNumber: number }
и другой{ myBool: boolean }
).4.
type PropsOf<T> = T extends React.ComponentType<infer Props> ? Props : never
Я видел, что это где-то использовалось для получения реквизитов переданных компонентов, и я пробовал такие вещи, какconst RequiresPermisson = < P extends Record<"PermittedComponent" | "NotPermittedComponent", unknown> >({
, и надеялся, что это заставит меня добавлятьPropsOf<P["PermittedComponent"]>
необходимые реквизиты. Может заставить его работать динамически :/