对象必须没有密钥

时间:2018-03-30 16:41:58

标签: flowtype

我试图定义一个对象必须没有某个键。

以下是我的案例:

alert({
   items: [{ label:'Apple' }, { label:'Orange' }]
})


alert({
   items: [{ foo:'Apple' }, { foo:'Orange' }]
   labelKey: 'foo'
})

如果items是不包含“label”键的对象数组,则选项中需要labelKey

我试过了:

type Options = {|
   items: Array<{ label:string }>
|} | {|
   items: Array<$Diff<{}, { label:string }>>,
   labelKey: string // must be key in items
|}

function alert(options: Options) {

}

奖金问题: 还可以定义labelKey是来自项目中传递的对象的任何键吗?

1 个答案:

答案 0 :(得分:2)

确保对象上不存在属性

tl; dr:使用{ myProp?: empty }

我假设您在将某些内容传递到alert函数时想要使用objects as maps。创建没有标签的地图的技巧是给出一个属性,如果分配给某个属性,将无法进行类型检查。

我们可以利用empty type,这是一种与任何东西都不匹配的类型,以获得理想的效果。将empty类型与对象映射结合使用有点棘手,因为通过定义属性,我们告诉流我们想要该类型在对象中。所以这没有成功:

Try

type MapWithLabel = {
  [string]: string,
  label: string,
}

type MapWithoutLabel = {[string]: mixed, label: empty}

type Options = {|
   items: Array<MapWithLabel>
|} | {|
   labelKey: string,
   items: Array<MapWithoutLabel>,
|}

declare function alert(options: Options): void;

alert({
   items: [{ foo:'Apple' }], // Error, expected a "label" property with empty type
   labelKey: 'foo'
})

接下来,我们可以将属性定义为optional,这意味着如果属性存在,则仅对empty进行类型检查。有了这个,我们可以给对象一个&#34;标签&#34;属性:

  • 不存在或
  • 有一个不匹配的类型(空)

因此代码可能没有该属性的值(我们想要的),或者它可以传递一些空的东西(这是不可能的)。

Try

type MapWithLabel = {
  [string]: string,
  label: string,
}

type MapWithoutLabel = {[string]: mixed, label?: empty}

type Options = {|
   items: Array<MapWithLabel>
|} | {|
   labelKey: string,
   items: Array<MapWithoutLabel>,
|}

declare function alert(options: Options): void;

alert({
  items: [{ label:'Apple' }],
})

alert({
  items: [{ label:'Apple' }], // Error - Should not have label
  labelKey: 'ohno',
})

alert({
   items: [{ foo:'Apple' }],
   labelKey: 'foo'
})

alert({
   items: [{ foo:'Apple' }], // Error - Needs a labelKey
}) 

为了获得预期的效果,我们需要利用两个工具:可选属性和empty类型。有了它,我们可以指定一个对象,如果empty属性存在,将无法进行类型检查。

在类型级别设置动态属性键

tl; dr:不可能

关于奖金问题:我不确定Flow能否理解,因为我不知道如何在对象上设置变量属性。我不希望这个功能,因为它可能使复杂/不可能键入检查。

编辑:经过一番研究后,可以使用indexer properties断言对象在类型级别有一个键:

Try

type ObjWithKey<T: string = 'label'> = {
  // An indexer property with only one valid value: T with "label"
  // as default, but we can't ensure that the property exists anymore
  // and multiple indexers are not supported.
  [T]: string,
  aNumber: 3,
  aFunction: () => void,
}

declare var usesLabel: ObjWithKey<>

(usesLabel.label: string);
(usesLabel.aNumber: number);
(usesLabel.missing: number); //Error - Doesn't exist on object
(usesLabel.aFunction: () => void);
(usesLabel.aFunction: string); //Error - Wrong type

但是,您不能这样做将该对象用作常规映射,因为不支持多个索引器属性(Try)。作为参考,其他人试图做其他类似的事情but couldn't get it to work

如果这对您来说是一个主要问题,请查看您是否可以采用不同的方式构建数据结构,以便更轻松地使用Flow进行静态分析。