如何在 Flow 中用多个可能的调用签名来注释一个函数?
How to annotate a function with multiple possible call signatures in Flow?
在 JavaScript 中,一个函数可以通过多种方式调用是很常见的——例如带有少量位置参数 或 单个选项对象 或 两者的某种组合。
我一直在想办法注释这个。
我尝试的一种方法是将 rest args 注释为各种可能的元组的联合:
type Arguments =
| [string]
| [number]
| [string, number]
;
const foo = (...args: Arguments) => {
let name: string;
let age: number;
// unpack args...
if (args.length > 1) {
name = args[0];
age = args[1];
} else if (typeof args[0] === 'string') {
name = args[0];
age = 0;
} else {
name = 'someone';
age = args[1];
}
console.log(`${name} is ${age}`);
};
// any of these call signatures should be OK:
foo('fred');
foo('fred', 30);
foo(30);
以上片段是人为设计的;在这个例子中我可能只使用 (...args: Array<string | number>)
,但是对于更复杂的签名(例如,涉及一个类型化的选项对象,它可以单独存在或与先前的参数一起使用),能够定义一个精确的、有限的集合将很有用可能的调用签名。
但是上面没有类型检查。您可以在 tryflow.
中看到一堆令人困惑的错误
我还尝试将函数本身键入为单独的整个函数定义的联合,但是 didn't work 要么:
type FooFunction =
| (string) => void
| (number) => void
| (string, number) => void
;
const foo: FooFunction = (...args) => {
let name: string;
let age: number;
// unpack args...
if (args.length > 1) {
name = args[0];
age = args[1];
} else if (typeof args[0] === 'string') {
name = args[0];
age = 0;
} else {
name = 'someone';
age = args[1];
}
console.log(`${name} is ${age}`);
};
// any of these call signatures should be OK:
foo('fred');
foo('fred', 30);
foo(30);
我应该如何使用多个可能的调用签名处理类型注释函数? (或者多重签名被认为是 Flow 中的反模式,我根本不应该这样做——在这种情况下,与这样做的第三方库交互的推荐方法是什么?)
在您给出的三种可能的锻炼中,我已经想出了如何使用单个选项对象使其工作,但是因为您需要至少设置一个对象,所以您需要定义每一种可能性。
像这样:
type Arguments =
{|
+name?: string,
+age?: number
|} |
{|
+name: string,
+age?: number
|} |
{|
+name?: string,
+age: number
|};
const foo = (args: Arguments) => {
let name: string = args.name ? args.name : 'someone';
let age: number = typeof args.age === 'number' && !isNaN(args.age) ? args.age : 0;
console.log(`${name} is ${age}`);
}
// any of these call signatures are OK:
foo({ name: 'fred' });
foo({ name: 'fred', age: 30 });
foo({ age: 30 });
// fails
foo({});
您看到的错误是代码中的错误和 Flow 中的错误的组合。
您的代码中存在错误
让我们从修复您的错误开始。在第三个 else 语句中,您将错误的值分配给
} else {
name = 'someone';
age = args[1]; // <-- Should be index 0
}
将数组访问更改为正确的索引可以消除两个错误。我想我们都同意这正是 Flow 的用途,即在您的代码中查找错误。
缩小型
为了找到问题的根本原因,我们可以在错误的地方更明确,这样我们就可以更容易地看到问题是什么:
if (args.length > 1) {
const args_tuple: [string, number] = args;
name = args_tuple[0];
age = args_tuple[1];
} else if (typeof args[0] === 'string') {
这实际上与以前相同,但因为我们非常清楚此时 args[0]
和 args[1]
应该是什么。这给我们留下了一个错误。
流程中的错误
剩下的错误是 Flow 中的错误:https://github.com/facebook/flow/issues/3564
bug: tuple type is not interacting with length assertions (.length >= 2 and [] | [number] | [number, number] type)
如何键入重载函数
Flow 不擅长处理不同类型的可变参数,就像在本例中一样。 Variadics 更适用于像 function sum(...args: Array<number>)
这样的东西,其中所有类型都相同并且没有最大数量。
Instead, you should be more explicit with your arguments, like so:
const foo = (name: string | number, age?: number) => {
let real_name: string = 'someone';
let real_age: number = 0;
// unpack args...
if (typeof name === 'number') {
real_age = name;
} else {
real_name = name;
real_age = age || 0;
}
console.log(`${real_name} is ${real_age}`);
};
// any of these call signatures should be OK:
foo('fred');
foo('fred', 30);
foo(30);
这不会导致错误,而且我认为开发人员也更容易阅读。
更好的方法
在另一个回答中,比我自己更喜欢
type Foo =
& ((string | number) => void)
& ((string, number) => void)
const foo: Foo = (name, age) => {...};
它以更简洁的方式解决了相同的问题,让您更加灵活。通过创建多个函数类型的交集,您可以描述调用函数的每种不同方式,从而允许 Flow 根据调用函数的方式尝试每一种方式。
您可以通过 &
:
连接它们来定义多个函数签名
type Foo =
& ((string | number) => void)
& ((string, number) => void)
在 JavaScript 中,一个函数可以通过多种方式调用是很常见的——例如带有少量位置参数 或 单个选项对象 或 两者的某种组合。
我一直在想办法注释这个。
我尝试的一种方法是将 rest args 注释为各种可能的元组的联合:
type Arguments =
| [string]
| [number]
| [string, number]
;
const foo = (...args: Arguments) => {
let name: string;
let age: number;
// unpack args...
if (args.length > 1) {
name = args[0];
age = args[1];
} else if (typeof args[0] === 'string') {
name = args[0];
age = 0;
} else {
name = 'someone';
age = args[1];
}
console.log(`${name} is ${age}`);
};
// any of these call signatures should be OK:
foo('fred');
foo('fred', 30);
foo(30);
以上片段是人为设计的;在这个例子中我可能只使用 (...args: Array<string | number>)
,但是对于更复杂的签名(例如,涉及一个类型化的选项对象,它可以单独存在或与先前的参数一起使用),能够定义一个精确的、有限的集合将很有用可能的调用签名。
但是上面没有类型检查。您可以在 tryflow.
中看到一堆令人困惑的错误我还尝试将函数本身键入为单独的整个函数定义的联合,但是 didn't work 要么:
type FooFunction =
| (string) => void
| (number) => void
| (string, number) => void
;
const foo: FooFunction = (...args) => {
let name: string;
let age: number;
// unpack args...
if (args.length > 1) {
name = args[0];
age = args[1];
} else if (typeof args[0] === 'string') {
name = args[0];
age = 0;
} else {
name = 'someone';
age = args[1];
}
console.log(`${name} is ${age}`);
};
// any of these call signatures should be OK:
foo('fred');
foo('fred', 30);
foo(30);
我应该如何使用多个可能的调用签名处理类型注释函数? (或者多重签名被认为是 Flow 中的反模式,我根本不应该这样做——在这种情况下,与这样做的第三方库交互的推荐方法是什么?)
在您给出的三种可能的锻炼中,我已经想出了如何使用单个选项对象使其工作,但是因为您需要至少设置一个对象,所以您需要定义每一种可能性。
像这样:
type Arguments =
{|
+name?: string,
+age?: number
|} |
{|
+name: string,
+age?: number
|} |
{|
+name?: string,
+age: number
|};
const foo = (args: Arguments) => {
let name: string = args.name ? args.name : 'someone';
let age: number = typeof args.age === 'number' && !isNaN(args.age) ? args.age : 0;
console.log(`${name} is ${age}`);
}
// any of these call signatures are OK:
foo({ name: 'fred' });
foo({ name: 'fred', age: 30 });
foo({ age: 30 });
// fails
foo({});
您看到的错误是代码中的错误和 Flow 中的错误的组合。
您的代码中存在错误
让我们从修复您的错误开始。在第三个 else 语句中,您将错误的值分配给
} else {
name = 'someone';
age = args[1]; // <-- Should be index 0
}
将数组访问更改为正确的索引可以消除两个错误。我想我们都同意这正是 Flow 的用途,即在您的代码中查找错误。
缩小型
为了找到问题的根本原因,我们可以在错误的地方更明确,这样我们就可以更容易地看到问题是什么:
if (args.length > 1) {
const args_tuple: [string, number] = args;
name = args_tuple[0];
age = args_tuple[1];
} else if (typeof args[0] === 'string') {
这实际上与以前相同,但因为我们非常清楚此时 args[0]
和 args[1]
应该是什么。这给我们留下了一个错误。
流程中的错误
剩下的错误是 Flow 中的错误:https://github.com/facebook/flow/issues/3564
bug: tuple type is not interacting with length assertions (.length >= 2 and [] | [number] | [number, number] type)
如何键入重载函数
Flow 不擅长处理不同类型的可变参数,就像在本例中一样。 Variadics 更适用于像 function sum(...args: Array<number>)
这样的东西,其中所有类型都相同并且没有最大数量。
Instead, you should be more explicit with your arguments, like so:
const foo = (name: string | number, age?: number) => {
let real_name: string = 'someone';
let real_age: number = 0;
// unpack args...
if (typeof name === 'number') {
real_age = name;
} else {
real_name = name;
real_age = age || 0;
}
console.log(`${real_name} is ${real_age}`);
};
// any of these call signatures should be OK:
foo('fred');
foo('fred', 30);
foo(30);
这不会导致错误,而且我认为开发人员也更容易阅读。
更好的方法
在另一个回答中,
type Foo =
& ((string | number) => void)
& ((string, number) => void)
const foo: Foo = (name, age) => {...};
它以更简洁的方式解决了相同的问题,让您更加灵活。通过创建多个函数类型的交集,您可以描述调用函数的每种不同方式,从而允许 Flow 根据调用函数的方式尝试每一种方式。
您可以通过 &
:
type Foo =
& ((string | number) => void)
& ((string, number) => void)