当我省略/需要道具时,为什么受歧视的工会不起作用?

cor*_*vid 5 javascript typescript reactjs

我有一个组件,它根据特定属性呈现不同的元素,称为type. 它可以有这样的类型定义:

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;
Run Code Online (Sandbox Code Playgroud)

现在在我自己的组件中,我将访问该组件的属性,如下所示:

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} />
}
Run Code Online (Sandbox Code Playgroud)

这绝对没问题。它知道我所在的if街区propsSelectInputProps。然而,我做了一个小小的改变,它似乎完全打破了这种使用受歧视联盟的模式。

type FieldProps = Omit<ComponentProps<typeof Field>, 'size'>;
Run Code Online (Sandbox Code Playgroud)

实际上,这是发生的事情

为什么会发生这种情况?有办法解决吗?

jca*_*alz 6

这是因为实用程序Omit<T, K>类型不会分布在. 该实现使用,并且当是联合时,仅是联合所有成员中存在的那些键(在 中是逆变的,因此相当于)。这按照microsoft/TypeScript#31501 的预期工作。Tkeyof TTkeyof Tkeyof TTkeyof (A | B)(keyof A) & (keyof B)

幸运的是,如果您想要 的分布式版本,您可以使用分布式条件类型Omit自己编写:

type DistributiveOmit<T, K extends PropertyKey> =  
  T extends any ? Omit<T, K> : never;
Run Code Online (Sandbox Code Playgroud)

然后你可以看到行为的变化:

type MyFieldProps = DistributiveOmit<React.ComponentProps<typeof Field>, 'a'>;
/* type MyFieldProps = Omit<{
    type: 'select';
    options: Option[];
} & ExtraProps, "a"> | Omit<{
    type: 'text';
    value: string;
} & ExtraProps, "a"> */
Run Code Online (Sandbox Code Playgroud)

这使您的代码开始工作:

function MyField(props: MyFieldProps) {
  if (props.type === 'select') {
    const options = props.options;    // okay
  }
  return <input />
}
Run Code Online (Sandbox Code Playgroud)

Playground 代码链接

function MyField(props: MyFieldProps) {
  if (props.type === 'select') {
    // Here's the problem
    const options = props.options;
  }
  return <input />
}
Run Code Online (Sandbox Code Playgroud)