在较高级别上,您可以重用 objectB 实例,只需对 ObjectA 类型稍作修改,也可以创建一个新对象。让我们深入了解您的选择:
将对象属性标记为协变
您要做的是将ObjectB 转换为ObjectA。 Flow 抱怨的原因是ObjectA 中foo 的类型默认为invariant。 ObjectB 的 foo 属性是 ObjectA 的 foo 属性的子类型。为了让 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
将属性标记为“协变”基本上表示您承诺只读取该属性,您不会写入它。看,如果您删除了 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>。