Я создаю приложение 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>
как props
, но это тоже не работает.
Как вы можете исправить это, чтобы либо передать реквизиты, либо разрешить любые (не типа any
) реквизиты, чтобы работал параметр ...rest
?
поскольку вы не принимаете никаких ответов, я бы предположил, что чего-то не хватает. Не могли бы вы дать какой-либо отзыв о том, на какую часть вашего вопроса не был дан ответ или почему он не соответствует вашему варианту использования?
Вам нужны универсальные типы из машинописного текста: 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 & T) =>
isPermitted ? (
<PermittedComponent {...rest} />
) : (
<NotPermittedComponent {...rest} />
);
export default RequiresPermisson;
Спасибо за вашу помощь. Я попробовал ваш код, он выдает ту же ошибку: 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'.
Боже, это сложная штука. Пытался создать способ передачи любого компонента как PermittedComponent
и NotPermittedComponent
и требовать передачи всех и только реквизитов компонента RequiresPermisson
. Искал по всему Интернету и, похоже, это не способ AFAIK. Проблема в динамических свойствах PermittedComponent
и NotPermittedComponent
, они ВСЕГДА содержат только {text: string}? В этом случае я был бы очень прост.
Я знаю правильно 😕 Спасибо, что использовали свои выходные, чтобы помочь мне, кстати, очень ценю это 🙏 Я также потратил несколько часов на это и не смог найти способ заставить это работать. Нет, PermittedComponent и NotPermittedComponent могут даже содержать оба разных реквизита (то есть один { myNumber: number }
, а другой { myBool: boolean }
).
type PropsOf<T> = T extends React.ComponentType<infer Props> ? Props : never
Я видел, что это где-то использовалось для получения реквизитов переданных компонентов, и я пробовал такие вещи, как const RequiresPermisson = < P extends Record<"PermittedComponent" | "NotPermittedComponent", unknown> >({
, и надеялся, что это заставит меня добавить PropsOf<P["PermittedComponent"]>
в качестве требуемых реквизитов. Может заставить его работать динамически:/
Не уверен, что это самое элегантное решение, но я только что объединил все типы реквизита. Это дает вам проверку типов для всех реквизитов. Я должен переслать все реквизиты, потому что я не могу убедиться, что что-то вроде isPermitted
не требуется для подкомпонента.
interface Props <A, B>{
NotPermittedComponent: React.ComponentType<A>;
PermittedComponent: React.ComponentType<B>;
isPermitted: boolean;
}
const RequiresPermisson = <A, B>(props: Props<A, B> & A & B): JSX.Element => {
const {
NotPermittedComponent,
PermittedComponent,
isPermitted,
} = props
return isPermitted ? (
<PermittedComponent {...props} />
) : (
<NotPermittedComponent {...props} />
)
}
А этот классный! Зачем нужны оба <A, B>
, а не только <A>
? @ian
A и B — это всего лишь реквизиты PermittedComponent и NotPermittedComponent, так как было предложено, чтобы они были разными, нам нужны для них разные типы. A и B будут выводиться из компонентов, которые вы вставляете, и все они будут необходимы/возможны в RequiersPermission. @Габриэль
Я принял ответ, потому что, по крайней мере, он компилируется, но технически есть еще одна проблема. PermittedComponent
и NotPermittedComponent
теперь получают себя и isPermitted
передаются им в качестве реквизита, но это не их реквизит. У вас есть какое-нибудь решение для этого?
Я понимаю, что вы беспокоитесь. Но даже с ...rest
из вашего кода вы также дадите PermittedComponent
реквизит NotPermittedComponent
. А что, если я напишу PermittedComponent
с isPermitted
в качестве опоры. Я не уверен, что есть способ разделить все это, не изолируя реквизиты во что-то вроде permittedComponentProps
или передавая их как renderProps.
разрешить любой реквизит" -
(props: any) => ...
?unknown
имеет больше смысла как тип возвращаемого значения, чем тип параметра.