admin管理员组文章数量:1414614
I have a ponent which renders different elements based on a specific property, called type
. It could have type definitions like this:
interface CommonProps {
size: 'lg' | 'md' | 'sm';
}
interface SelectInputProps extends CommonProps {
type: 'select';
options: readonly Option[];
selected: string;
}
interface TextInputProps extends CommonProps {
type: 'text';
value: string;
};
type InputProps = (SelectInputProps | TextInputProps) & ExtraProps;
function Field(props: InputProps): JSX.Element;
Now in my own ponent, I will access the properties of this ponent, like so:
import { ComponentProps } from 'react';
type FieldProps = ComponentProps<typeof Field>;
function MySpecialField(props: FieldProps) {
if (props.type === 'select') {
// this works
const { options, selected } = props;
}
return <Field {...props} />
}
This works absolutely fine. It knows in my if
block that props
is SelectInputProps
. However, I made one small change and it appeared to pletely break this mode of using the discriminated union.
type FieldProps = Omit<ComponentProps<typeof Field>, 'size'>;
In practice, here is what is happening
Why is this happening? Is there a way to fix it?
I have a ponent which renders different elements based on a specific property, called type
. It could have type definitions like this:
interface CommonProps {
size: 'lg' | 'md' | 'sm';
}
interface SelectInputProps extends CommonProps {
type: 'select';
options: readonly Option[];
selected: string;
}
interface TextInputProps extends CommonProps {
type: 'text';
value: string;
};
type InputProps = (SelectInputProps | TextInputProps) & ExtraProps;
function Field(props: InputProps): JSX.Element;
Now in my own ponent, I will access the properties of this ponent, like so:
import { ComponentProps } from 'react';
type FieldProps = ComponentProps<typeof Field>;
function MySpecialField(props: FieldProps) {
if (props.type === 'select') {
// this works
const { options, selected } = props;
}
return <Field {...props} />
}
This works absolutely fine. It knows in my if
block that props
is SelectInputProps
. However, I made one small change and it appeared to pletely break this mode of using the discriminated union.
type FieldProps = Omit<ComponentProps<typeof Field>, 'size'>;
In practice, here is what is happening
Why is this happening? Is there a way to fix it?
Share Improve this question asked Jun 1, 2021 at 18:36 corvidcorvid 11.2k12 gold badges71 silver badges134 bronze badges1 Answer
Reset to default 10It's because the Omit<T, K>
utility type does not distribute over union types in T
. The implementation uses keyof T
, and when T
is a union, keyof T
is only those keys that exist in all members of the union (keyof T
is contravariant in T
, so keyof (A | B)
is equivalent to (keyof A) & (keyof B)
). This is working as intended as per microsoft/TypeScript#31501.
Luckily, if you want a distributive version of Omit
, you can write it yourself using a distributive conditional type:
type DistributiveOmit<T, K extends PropertyKey> =
T extends any ? Omit<T, K> : never;
And then you can see the change in behavior:
type MyFieldProps = DistributiveOmit<React.ComponentProps<typeof Field>, 'a'>;
/* type MyFieldProps = Omit<{
type: 'select';
options: Option[];
} & ExtraProps, "a"> | Omit<{
type: 'text';
value: string;
} & ExtraProps, "a"> */
which makes your code start working:
function MyField(props: MyFieldProps) {
if (props.type === 'select') {
const options = props.options; // okay
}
return <input />
}
Playground link to code
function MyField(props: MyFieldProps) {
if (props.type === 'select') {
// Here's the problem
const options = props.options;
}
return <input />
}
本文标签: javascriptWhy doesn39t discriminated union work when I omitrequire propsStack Overflow
版权声明:本文标题:javascript - Why doesn't discriminated union work when I omitrequire props? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745173723a2646130.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论