在 @flow strict 下,对于实用函数,对象有哪些替代方案?
Under @flow strict, what alternatives are there to Object for utility functions?
我有兴趣将我的 Flow 代码切换到 strict
类型检查,但我有一些低级实用函数可以通用地处理对象,例如:
// @flow strict
const hasKey = (o: Object): (string => boolean) =>
Object.prototype.hasOwnProperty.bind(o);
const union = (os: Array<Object>): Object =>
os.reduceRight((acc, o) => ({ ...acc, ...o }), {});
由于在严格模式下不允许使用 Object 类型,您如何为明确应该对任何通用 Object 进行操作的函数声明类型?
也许您可以使用流泛型编写您的函数。例如,
function union <T>(os: Array<T>): T {
...
}
在这种情况下,您实际上会从更严格的输入中受益匪浅。通过使用 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: {}>
中的: {}
。这些是 类型范围。 如果泛型是一种表达方式,"allow the user to pass any type they want, and store that type in a variable so that I can refer to it later," 那么类型 范围 是一种表达方式 "allow the user to pass any type they want, as long as that type is a member of type X."
由于 width subtyping 的工作方式,{}
是最通用的对象类型。实际上所有对象都是 {}
的子类型。所以 <T: {}>
基本上意味着 "T should be any type that is an object."
请注意,这与 <T: Object>
非常不同,后者基本上意味着,"T is an object and from now on I'm not checking anything else about it." 这意味着我们可以执行以下操作:
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。它有属性,它可以是 rest 和 spread,它可以是字符串索引等等, 但没有别的。 此外,通过将数据类型存储为通用 T
和返回该类型,我们正在维护参数的类型信息。这意味着无论我们作为参数传入什么,我们都会从另一边得到相同类型的东西(而不是神秘的黑匣子)。
我有兴趣将我的 Flow 代码切换到 strict
类型检查,但我有一些低级实用函数可以通用地处理对象,例如:
// @flow strict
const hasKey = (o: Object): (string => boolean) =>
Object.prototype.hasOwnProperty.bind(o);
const union = (os: Array<Object>): Object =>
os.reduceRight((acc, o) => ({ ...acc, ...o }), {});
由于在严格模式下不允许使用 Object 类型,您如何为明确应该对任何通用 Object 进行操作的函数声明类型?
也许您可以使用流泛型编写您的函数。例如,
function union <T>(os: Array<T>): T {
...
}
在这种情况下,您实际上会从更严格的输入中受益匪浅。通过使用 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: {}>
中的: {}
。这些是 类型范围。 如果泛型是一种表达方式,"allow the user to pass any type they want, and store that type in a variable so that I can refer to it later," 那么类型 范围 是一种表达方式 "allow the user to pass any type they want, as long as that type is a member of type X."
由于 width subtyping 的工作方式,{}
是最通用的对象类型。实际上所有对象都是 {}
的子类型。所以 <T: {}>
基本上意味着 "T should be any type that is an object."
请注意,这与 <T: Object>
非常不同,后者基本上意味着,"T is an object and from now on I'm not checking anything else about it." 这意味着我们可以执行以下操作:
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。它有属性,它可以是 rest 和 spread,它可以是字符串索引等等, 但没有别的。 此外,通过将数据类型存储为通用 T
和返回该类型,我们正在维护参数的类型信息。这意味着无论我们作为参数传入什么,我们都会从另一边得到相同类型的东西(而不是神秘的黑匣子)。