I have these types:
export enum LayersItemOptionsEnum {
OPERATOR,
HEADER,
}
type sharedTypes = {
children: string | ReactElement;
};
type LayersItemStatic = sharedTypes & {
label: string;
option: LayersItemOptionsEnum;
};
type LayersItemDynamic = sharedTypes & {
currentLayer: LayersElement;
};
export type LayersItemProps = (LayersItemDynamic | LayersItemStatic) & sharedTypes;
I am trying to use them like so:
export const LayersItem: FC<LayersItemProps> = (props): ReactElement => {
const isHeader = props.option === LayersItemOptionsEnum.HEADER;
const { isInEditMode } = useAppSelector((state) => state.editMode);
const shouldRenderOptions = isInEditMode && !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>
);
};
But I am getting the errors like this one:
Property 'label' does not exist on type 'PropsWithChildren<LayersItemProps>'.
Property 'label' does not exist on type 'sharedTypes & { currentLayer: LayersElement; } & { children?: ReactNode; }'.
For each of the props.
apart from props.children
. Like it doesn't see the union in types. Or am I misunderstanding something?
Basically, if the props have label
, or option
, I want props
to be of type LayersItemStatic & shared Types
, and if there is currentLayer
in props
, I want them to be of type LayersItemDynamic & sharedTypes
.
So what am I missing here?
I am trying to achieve something like this:
type SharedType = SharedDisplayAndEditTypes & {
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) & SharedType;
CodePudding user response:
Consider this example:
import { ReactElement } from 'react'
type LayersElement = {
tag: 'LayersElement'
}
export enum LayersItemOptionsEnum {
OPERATOR,
HEADER,
}
type sharedTypes = {
children: string | ReactElement;
};
type LayersItemStatic = sharedTypes & {
label: string;
option: LayersItemOptionsEnum;
};
type LayersItemDynamic = sharedTypes & {
currentLayer: LayersElement;
};
export type LayersItemProps = (LayersItemDynamic | LayersItemStatic) & sharedTypes;
declare var props: LayersItemProps;
props.children // ok
Only children
prop is allowed because it is a common prop for each element of the union.
See Best common type
Since nobody know which elem of the union is actually allowed TS decides to allow you only properties which are safe for each element of the union.
Consider this smaller example:
type LayersItemStatic = {
label: string;
option: string;
};
type LayersItemDynamic = {
currentLayer: string;
};
export type LayersItemProps = LayersItemDynamic | LayersItemStatic
declare var props: LayersItemProps;
Because there are no common props, you are not allowed to use any prop.
I don't think that this type is correct:
export type LayersItemProps = (LayersItemDynamic | LayersItemStatic) & sharedTypes
Since LayersItemDynamic | LayersItemStatic
is reduced to {}
and LayersItemProps
basically equals to sharedTypes
.
Since you already added & sharedType
to both LayersItemDynamic | LayersItemStatic
you need rewrite your type LayersItemProps
as follow:
import { ReactElement } from 'react'
type LayersElement = {
tag: 'LayersElement'
}
export enum LayersItemOptionsEnum {
OPERATOR,
HEADER,
}
type sharedTypes = {
children: string | ReactElement;
};
type LayersItemStatic = sharedTypes & {
label: string;
option: LayersItemOptionsEnum;
};
type LayersItemDynamic = sharedTypes & {
currentLayer: LayersElement;
};
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & 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
}