типы союзов, не признаваемые в React

#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 к типам.