#reactjs #typescript
#reactjs #typescript
Вопрос:
У меня есть довольно сложный компонент React в приложении на основе Typescript. Существует несколько групп реквизитов, которые должны требоваться только при наличии другого реквизита / true. Есть ли какой-нибудь способ выполнить это в Typescript? Я еще не слишком продвинулся в наборе текста с ним.
Я предпринял несколько попыток с разделенными объединениями, но я не думаю, что это полностью охватывает все возможности, поскольку возможно, что потребуется любая комбинация или ни одна из этих дополнительных групп реквизитов.
Это то, с чем я здесь работаю.
interface CommonProps {
...a bunch of props...
}
interface ManagedProps extends CommonProps {
managed: true;
...props for only when managed is present/true...
}
interface ServerSideProps extends CommonProps {
serverSide: true;
...props for only when serverSide is present/true...
}
interface Props = ???
Базовым является то, что CommonProps
будет применяться к этому компоненту. Если managed
есть true
, то ManagedProps
также должно применяться. Если serverSide
есть true
, то ServerSideProps
также применяется.
У меня пока нет такой ситуации, но было бы неплохо, если бы необязательные группы реквизитов могли переопределять реквизит, определенный в CommonProps
.
Ответ №1:
Я добился большого успеха, используя помощник ts-toolbelt Union.Strict
от,,ts-toolbelt».
Сначала давайте определим два ваших отдельных набора возможных типов, объединим их в строгом объединении и напишем защиту типа, которая сообщает нашему компоненту React, какой набор реквизитов мы используем. Я расширю пример, который вы использовали в своей операции:
import { Union } from 'ts-toolbelt';
interface CommonProps {
commonProp: string
}
interface ManagedProps extends CommonProps {
managed: true;
managedBoolean: boolean;
managedNumber: number;
}
interface ServerSideProps extends CommonProps {
serverSide: true;
serverBoolean: boolean;
serverNumber: number;
}
const isManaged = (props: Props): props is ManagedProps => {
return props.managed;
}
type Props = Union.Strict<ManagedProps | ServerSideProps>
Теперь давайте напишем компонент React, который использует защиту типов, которую мы написали:
const OurComponent: React.FC<Props> = (props) => {
if (isManaged(props)) {
return <div>Managed {props.commonProp}</div>
} else {
return <div>ServerSide {props.commonProp}</div>
}
}
Это приведет к ошибкам, если вы используете неправильные типы. Вы можете проверить это в этом StackBlitz, который я создал.
Ответ №2:
Вы могли бы использовать условные типы, чтобы получить что-то вроде этого. Например:
interface CommonProps {
foo: string;
}
interface ManagedProps extends CommonProps {
managed: true;
managedOnlyProp: boolean;
}
interface ServerSideProps extends CommonProps {
serverSide: true;
serverOnlyProp: boolean;
}
type MappedProps<T> =
T extends { managed: true } ? ManagedProps :
T extends { serverSide: true } ? ServerSideProps :
CommonProps;
// Example function to test this:
declare function takeProps<T>(props: T amp; MappedProps<T>): void;
// And:
// This is OK, neither flag is present, so CommonProps are used:
takeProps({ foo: "" })
// This is an error. The managed flag is present, so we must also include the managed only props:
takeProps({ foo: "", managed: true }) // Gives error: Property 'managedOnlyProp' is missing in type '{ foo: string; managed: true; }' but required in type 'ManagedProps'.
// But this is okay, the flag is present but set to false...
takeProps({ foo: "", managed: false })
// And likewise for the serverSide versions:
takeProps({ foo: "", serverSide: true }) // Gives error: Property 'serverOnlyProp' is missing in type '{ foo: string; serverSide: true; }' but required in type 'ServerSideProps'.
takeProps({ foo: "", serverSide: true, serverOnlyProp: false }) // OK, no errors
takeProps({ foo: "", serverSide: false }) // OK, no errors
Вот <a rel="noreferrer noopener nofollow" href="https://www.typescriptlang.org/play/#src=interface CommonProps {
foo: string;
}
interface ManagedProps extends CommonProps {
managed: true;
managedOnlyProp: boolean;
}
interface ServerSideProps extends CommonProps {
serverSide: true;
serverOnlyProp: boolean;
}
type MappedProps =
T extends { managed: true } ? ManagedProps :
T extends { serverSide: true } ? ServerSideProps :
CommonProps;
// Example function to test this:
declare function takeProps(props: T & MappedProps): void;
// And:
// This is OK, neither flag is present, so CommonProps are used:
takeProps({ foo: «» })
// This is an error. The managed flag is present, so we must also include the managed only props:
takeProps({ foo: «», managed: true }) // Gives error: Property ‘managedOnlyProp’ is missing in type ‘{ foo: string; managed: true; }’ but required in type ‘ManagedProps’.
// But this is okay, the flag is present but set to false…
takeProps({ foo: «», managed: false })
// And likewise for the serverSide versions:
takeProps({ foo: «», serverSide: true }) // Gives error: Property ‘serverOnlyProp’ is missing in type ‘{ foo: string; serverSide: true; }’ but required in type ‘ServerSideProps’.
takeProps({ foo: «», serverSide: true, serverOnlyProp: false }) // OK, no errors
takeProps({ foo: «», serverSide: false }) // OK, no errors
» rel=»nofollow noreferrer»>ссылка на игровую площадку.
Обратите внимание, что есть предостережение в том, что это работает только для каждого флага в отдельности. При использовании в комбинации вы все равно можете передать недопустимый объект:
// This is wrong (missing serverOnlyProp) but no error is produced... :(
takeProps({foo: "", serverSide: true, managed: true, managedOnlyProp: false})