作者:100斤的重口味_866 | 来源:互联网 | 2023-02-05 12:29
在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)
在这个例子中使用,但是对于更复杂的签名(例如涉及可以单独或与先前args的类型化选项对象),能够定义一组精确的,有限的可能的呼叫签名将是有用的.
但上面没有打字检查.您可以在tryflow中看到一堆令人困惑的错误.
我也尝试将函数本身键入为单独的整个函数defs的联合,但这也不起作用:
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中的反模式,我根本不应该这样做 - 在这种情况下,与第三方库进行交互的推荐方法是什么?)
1> EugeneZ..:
您看到的错误是代码中的错误和Flow中的错误的组合.
代码中的错误
让我们从修复你的bug开始吧.在第三个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中的错误
剩下的错误是Flow中的错误:https://github.com/facebook/flow/issues/3564
bug:元组类型不与长度断言交互(.length> = 2和[] | [number] | [number,number]类型)
如何键入重载的函数
在处理具有不同类型的可变参数时,流程并不是很好,就像在这种情况下一样.Variadics更适用于function sum(...args: Array)
所有类型相同而且没有最大arity的东西.
相反,您应该更明确地使用您的参数,如下所示:
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);
这不会导致错误,我认为开发人员也更容易阅读.
一个更好的方法
在另一个答案中,Pavlo提供了另一种我喜欢的解决方案.
type Foo =
& ((string | number) => void)
& ((string, number) => void)
const foo: Foo = (name, age) => {...};
它以更清洁的方式解决了同样的问题,让您更灵活.通过创建多个函数类型的交集,您可以描述调用函数的每种不同方式,允许Flow根据函数的调用方式尝试每个函数.
2> Pavlo..:
您可以通过以下方式连接它们来定义多个功能签名&
:
type Foo =
& ((string | number) => void)
& ((string, number) => void)
试试吧.