【问题标题】:Using `Omit` on a type with an index signature loses all non-index signature field declarations在具有索引签名的类型上使用“省略”会丢失所有非索引签名字段声明
【发布时间】:2019-10-05 17:41:01
【问题描述】:

我正在编写一个包装 react-select 的 Select 组件的 React 组件,该组件有一个 starts like this 的 prop 声明:

export type SelectComponentsProps = { [key in string]: any };

export interface Props<OptionType extends OptionTypeBase = { label: string; value: string }> extends SelectComponentsProps {

我导入了这个类型,并试图像这样声明我的包装器组件的道具:

import { Props as SelectProps } from "react-select/src/Select";
type Props = Omit<SelectProps, "inputId"> & { ... some other stuff };

但我遇到了一个问题,我可以“安全地”将任何我想要的东西传递给我的组件,即使对于在 SelectProps 上具有显式类型声明的字段:

// okay
<MySelectWrapper onChange="definitely not a function"/>

// not okay, fails type checking
<Select onChange="definitely not a function"/>

经过一番挖掘,我发现Omit 和索引签名的组合(在 react-select 示例中:SelectComponentsProps)导致编译器删除显式指定的字段,而只使用索引签名。

interface ArbitraryKeyable {
  foo: number;
  [key: string]: any;
}

const value = {
  foo: 'not a number',
  bar: 'this can be anything'
}

// Fails: foo is the wrong type.
const arbitrary: ArbitraryKeyable = value;

type OmittedArbitraryKeyable = Omit<ArbitraryKeyable, 'this is not a field'>;

// Succeeds, even though the type should functionally be the same.
const omittedArbitrary: OmittedArbitraryKeyable = value;

(playground link)

游乐场认为该类型是这样的:

所以它当然接受一切!有没有办法定义一个保留显式定义字段的Omit 版本?或者,是否可以执行类型操作来删除索引签名并且只有显式字段? (我的用例并不需要它,所以我可以放弃这种灵活性以在其他地方获得类型安全。)

【问题讨论】:

    标签: typescript


    【解决方案1】:

    Omit 与索引签名组合将不起作用,因为它在内部使用 keyof 运算符。当您将keyof 应用于具有索引签名的类型时,它只会返回索引的类型,而不包括显式成员:

    interface ArbitraryKeyable {
      foo: number;
      [key: string]: any;
    }
    
    type Keys = keyof ArbitraryKeyable // type Keys = string | number (no "foo")
    

    并且从字符串索引签名中排除属性"foo" 仍然保留索引。

    type S1 = Omit<ArbitraryKeyable, 'foo'>
    type S2 = Pick<ArbitraryKeyable, Exclude<keyof ArbitraryKeyable, "foo">>;
    type S3 = Pick<ArbitraryKeyable, Exclude<string| number, "foo">>; 
    type S4 = Pick<ArbitraryKeyable, string | number>; // { [x: string]: any; [x: number]: any;}
    

    There is a way 删除索引签名,但如果你问我,感觉有点骇人听闻。在适用的情况下,通过显式选择所有所需的属性可能会更好。这还有一个好处是您可以更好地控制和缩小外部 API。

    // instead of omit...
    type Props = Omit<SelectProps, "inputId"> & { ... some other stuff }
    
    // ... pick the props
    type Props = Pick<SelectProps, "prop1" | "prop2">
    

    【讨论】:

      猜你喜欢
      • 2018-12-15
      • 2017-06-22
      • 2020-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-06
      • 2021-08-05
      • 2022-01-26
      相关资源
      最近更新 更多