我有兴趣将我的Flow代码切换为project.clj
类型检查,但是我有一些通用的对象处理底层工具,例如:
strict
由于在严格模式下不允许使用Object类型,因此如何为明确应该在任何通用Object上运行的函数声明类型?
答案 0 :(得分:4)
在这种情况下,实际上您将从严格的键入中受益匪浅。通过使用Object
,您实际上将关闭所有通过这些函数的数据的键入系统 ,直到可以在其他位置显式重新键入它们为止。这意味着您当前正在丢失大量不需要的键入信息。
这是泛型的教科书案例,已记录在here中。
// @flow strict
const hasKey = <T: {}>(o: T): (string => boolean) =>
Object.prototype.hasOwnProperty.bind(o);
const union = <T: {}>(objects: Array<T>): T =>
objects.reduce((acc, o) => ({ ...acc, ...o }), ({}: $Shape<T>));
上面最重要的部分是: {}
中的<T: {}>
。这些是类型范围。如果泛型是一种说法,即“允许用户传递他们想要的任何类型,并将该类型存储在变量中,以便我以后可以引用它”,那么 bounds 类型是一种说法,“允许用户传递他们想要的任何类型,只要该类型是X类型的成员。”
由于width subtyping的工作方式,{}
是最通用的对象类型。实际上,所有对象都是{}
的子类型。所以<T: {}>
的基本含义是,“ T应该是作为对象的任何类型。”
请注意,这与<T: Object>
有很大不同,这基本上意味着:“ T是一个对象,从现在开始,我将不再检查它。”这意味着我们可以执行以下操作:
const o: Object = {};
console.log(o.some.member.that.doesnt.exist); // no error at compile time,
// but obvious error at runtime
不一样:
const o: {} = {};
console.log(o.member); // Error, we don't know about this member property!
因此,通过告诉流程该参数是{}
的子类型,我们告诉它它具有对象的基本API。它具有属性,可以休息和散布,可以进行字符串索引等,但是别无其他。此外,通过将数据类型存储为通用T
并返回该类型,我们正在维护参数的类型信息。这意味着无论我们传入什么参数,都将在另一端得到相同类型的东西(而不是神秘的黑盒子)。
答案 1 :(得分:0)
也许您可以使用流泛型编写函数。例如,
function union <T>(os: Array<T>): T {
...
}