#javascript #reactjs #typescript #types #discriminated-union
Вопрос:
У меня есть такие типы:
export enum LayersItemOptionsEnum {
OPERATOR,
HEADER,
}
type sharedTypes = {
children: string | ReactElement;
};
type LayersItemStatic = sharedTypes amp; {
label: string;
option: LayersItemOptionsEnum;
};
type LayersItemDynamic = sharedTypes amp; {
currentLayer: LayersElement;
};
export type LayersItemProps = (LayersItemDynamic | LayersItemStatic) amp; sharedTypes;
Я пытаюсь использовать их так:
export const LayersItem: FC<LayersItemProps> = (props): ReactElement => {
const isHeader = props.option === LayersItemOptionsEnum.HEADER;
const { isInEditMode } = useAppSelector((state) => state.editMode);
const shouldRenderOptions = isInEditMode amp;amp; !isHeader;
const { selectedState } = useAppSelector((state) => state);
const states = useAppSelector((state) => state.statesData.elements);
return (
<StyledLayersItem header={isHeader}>
<Row>
<Col span={8} offset={1 /* todo: add offset dynamically */}>
<h1>{props.label ? props.label : props.currentLayer.name}</h1>
</Col>
<Col span={8} offset={4}>
{shouldRenderOptions ? (
<Form.Item className="form-item" initialValue={props.children}>
<Select>
{generateOptions({ selectedState, states, props.currentLayer }).map((value) => {
return (
<Select.Option value={value.id} key={value.id}>
{value.name}
</Select.Option>
);
})}
</Select>
</Form.Item>
) : (
<>{props.children}</>
)}
</Col>
</Row>
</StyledLayersItem>
);
};
Но я получаю ошибки, подобные этой:
Property 'label' does not exist on type 'PropsWithChildren<LayersItemProps>'.
Property 'label' does not exist on type 'sharedTypes amp; { currentLayer: LayersElement; } amp; { children?: ReactNode; }'.
Для каждого из props.
отдельно props.children
взятых . Как будто он не видит союза в типах. Или я чего-то не понимаю?
В принципе, если у реквизита есть label
или option
я хочу props
быть типичным LayersItemStatic amp; shared Types
, и если есть currentLayer
props
, я хочу , чтобы они были типичными LayersItemDynamic amp; sharedTypes
.
Так чего же мне здесь не хватает?
Я пытаюсь достичь чего-то подобного:
type SharedType = SharedDisplayAndEditTypes amp; {
required?: boolean;
validationMessage: string;
name: string;
};
type TextType = {
type: 'text';
children: string;
};
type NumberType = {
type: 'number';
children: number;
};
type InputType = TextType | NumberType;
type DropdownType = {
type: 'dropdown';
options: string[];
children: string;
};
type ColorType = {
type: 'color';
defaultValue: string;
};
export type DetailsItemEditProps = (DropdownType | InputType | ColorType) amp; SharedType;
Комментарии:
1. Пожалуйста, либо поделитесь воспроизводимым примером, либо укажите строку, в которой у вас есть ошибка. Если у вас есть объединение объектов, TS позволит вам использовать только общие свойства. Если какое-либо свойство является специфичным только для одного элемента объединения, вам следует создать свой собственный типографский указатель, чтобы использовать это свойство элемента
2. Эта ошибка возникает в каждом месте, куда я звоню
props.something
. Помимоprops.children
Ответ №1:
Рассмотрим этот пример:
import { ReactElement } from 'react'
type LayersElement = {
tag: 'LayersElement'
}
export enum LayersItemOptionsEnum {
OPERATOR,
HEADER,
}
type sharedTypes = {
children: string | ReactElement;
};
type LayersItemStatic = sharedTypes amp; {
label: string;
option: LayersItemOptionsEnum;
};
type LayersItemDynamic = sharedTypes amp; {
currentLayer: LayersElement;
};
export type LayersItemProps = (LayersItemDynamic | LayersItemStatic) amp; sharedTypes;
declare var props: LayersItemProps;
props.children // ok
Допускается только children
опора, поскольку она является общей опорой для каждого элемента объединения.
Смотрите Лучший распространенный тип
Поскольку никто не знает, какой элемент объединения на самом деле разрешен, TS решает разрешить вам только те свойства, которые безопасны для каждого элемента объединения.
Рассмотрим этот небольшой пример:
type LayersItemStatic = {
label: string;
option: string;
};
type LayersItemDynamic = {
currentLayer: string;
};
export type LayersItemProps = LayersItemDynamic | LayersItemStatic
declare var props: LayersItemProps;
Поскольку обычных реквизитов нет, вам не разрешается использовать какие-либо реквизиты.
Я не думаю, что этот тип правильный:
export type LayersItemProps = (LayersItemDynamic | LayersItemStatic) amp; sharedTypes
Поскольку LayersItemDynamic | LayersItemStatic
сводится к {}
и LayersItemProps
в основном равно sharedTypes
.
Поскольку вы уже добавили amp; sharedType
в оба LayersItemDynamic | LayersItemStatic
, вам нужно переписать свой тип LayersItemProps
следующим образом:
import { ReactElement } from 'react'
type LayersElement = {
tag: 'LayersElement'
}
export enum LayersItemOptionsEnum {
OPERATOR,
HEADER,
}
type sharedTypes = {
children: string | ReactElement;
};
type LayersItemStatic = sharedTypes amp; {
label: string;
option: LayersItemOptionsEnum;
};
type LayersItemDynamic = sharedTypes amp; {
currentLayer: LayersElement;
};
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj amp; Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
export type LayersItemProps = LayersItemDynamic | LayersItemStatic
const isDynamic = (props: LayersItemProps): props is LayersItemDynamic => hasProperty(props, 'currentLayer')
const isStatic = (props: LayersItemProps): props is LayersItemStatic => hasProperty(props, 'label')
declare var props: LayersItemProps;
if (isDynamic(props)) {
props.currentLayer // ok
}
if (isStatic(props)) {
props.label // ok
props.option // ok
}
Комментарии:
1. Хорошо, я понимаю вашу точку зрения. Можете ли вы проверить вопрос? Я добавлю фрагмент кода, который делает что-то похожее на то, чего я пытаюсь достичь, но простым способом. Не могли бы вы, пожалуйста, сказать мне, почему это работает там, но не в моем случае? Типы имеют только один общий элемент, но ts может определить, какой тип является тем или иным на основе этого одного элемента
2. Хорошо, я смог ответить на свой собственный вопрос, добавив
type: 'static'
иtype: dynamic
к типам.