javascript - 深度子类型的流错误

标签 javascript ecmascript-6 flowtype

根据 Subtypes of objects 文档的流程,这是有效的

// @flow
type ObjectA = { foo: string };
type ObjectB = { foo: string, bar: number };

let objectB: ObjectB = { foo: 'test', bar: 42 };
let objectA: ObjectA = objectB; // Works!

但是更深层次的实现并没有

// @flow
type ObjectA = { foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB; // Error! Why?

有什么想法吗?

最佳答案

在高层次上,您可以通过对 ObjectA 类型进行轻微修改来重用 objectB 实例,也可以创建一个新对象。让我们深入了解您的选择:

将对象属性标记为协变

您要做的是将 ObjectB 转换为 ObjectA 。 Flow 提示的原因是 fooObjectA 的类型默认是 invariantfooObjectB 属性是 fooObjectA 属性的子类型。为了让 Flow 理解这一点,我们只需要将 foo 属性标记为 covariant :

( Try )

// @flow
type ObjectA = { +foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB; // Woohoo, no error

将属性标记为“协变”基本上表示您 promise 只读取该属性,不会写入它。看,如果您删除了 objectA 的 baz 属性,它会从 objectB 中删除 baz。通过将其标记为协变,Flow 将在您写入时抛出错误:

( Try )

// @flow
type ObjectA = { +foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB;

objectA.foo = {bar: 'oh-oh, deleted baz in objectB'}; //Error

此模式也适用于嵌套更深的对象:

( Try )

// @flow
type ObjectA = { +foo: { +bar: { baz: string } } };
type ObjectB = { foo: { bar: { bax: string, baz: string } } };

let objectB: ObjectB = { foo: { bar: { bax: '123', baz: '456' } } };
let objectA: ObjectA = objectB; // Woohoo, no error

有关此类型的更多详细信息,请参阅 depth subtyping 上的 Flow 文档。

使用 $ReadOnly<T>

Flow 有一个实用类型 $ReadOnly<T> ,用于将对象的所有属性标记为协变的,因此您可能想改用它:

( Try )

// @flow
type ObjectA = $ReadOnly<{ foo: { bar: string } }>;
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = objectB; // Woohoo, no error

或者您可以创建 ObjectA 的 ReadOnly 实例并单独保留 ObjectA 定义:

( Try )

// @flow
type ObjectA = { foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: $ReadOnly<ObjectA> = objectB; // Woohoo, no error

创建一个新对象

或者,您可以使用展开创建对象的新副本并避免所有这些输入:

( Try )

// @flow
type ObjectA = { foo: { bar: string } };
type ObjectB = { foo: { bar: string, baz: string } };

let objectB: ObjectB = { foo: { bar: '123', baz: '456' } };
let objectA: ObjectA = {...objectB} // Create a new object

但这只会在一层深度起作用,并且会创建一个额外的对象。通常,当我需要以只读方式使用对象时,我会跳过此选项并最终使用 $ReadOnly<T>

关于javascript - 深度子类型的流错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50145934/

相关文章:

javascript - Flow Array Generic 与其自身不兼容?

javascript - 在 IE11 中循环

javascript - 正则表达式测试无法判断真假(JavaScript)

Javascript ES6 yield 与 python 中的 yield

javascript - ios addEventListener 不工作

redux - 如何正确输入传奇

javascript - 如何在选择框被选中时重新加载页面

javascript - 如何编写 angularJs Controller 以从 Parse.com 获取 Rest 数据

javascript - ES6 Proxy 记录了无法解释的额外值

flowtype - flowjs中的奇怪错误消息 "inconsistent use of library definitions"