在JavaScript中,通常具有以多种方式调用的函数是很常见的 - 例如具有少数位置参数或一个单一选项对象或两者的某些组合。
我一直在试图弄清楚如何注释。
我尝试的一种方法是注释休息作为各种可能元组的结合:
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>)
,但是对于更复杂的签名(例如,涉及一个可以单独或使用先前ARG的键入选项对象(,能够定义一组可能的呼叫签名,这将是有用的。
,但上面没有类型检查。您可以看到一堆令人困惑的错误。
我还尝试将函数本身作为单独的整个函数def的结合键入,但这也不起作用:
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);
我应该如何使用多个可能的呼叫签名来处理通知功能?(或多签名是流动中的反标题,我根本不应该这样做 - 在这种情况下,与执行此操作的第三方库相互作用的建议方法是什么?(
您看到的错误是代码中的错误的组合和流的错误。
您的代码中的错误
让我们从修复错误开始。在第三个语句中,您将错误的值分配给
} else {
name = 'someone';
age = args[1]; // <-- Should be index 0
}
将数组访问更改为正确的索引,将删除两个错误。我认为我们俩都可以同意这正是流程的目的,在您的代码中找到错误。
缩小类型
为了解决问题的根本原因,我们可以在错误的区域更明确,以便我们可以更轻松地看到问题是什么:
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]
在这一点上应该是什么。这给我们留下了一个错误。
流中的错误
剩余的错误是流的错误:https://github.com/facebook/flow/sissues/3564
错误:元组类型不是与长度断言(.length> = 2和[] | [number] | [numborn,number]类型(
如何键入过载函数
流动并不是在处理具有不同类型的变异性的情况下,如在这种情况下。variadics更多地用于诸如function sum(...args: Array<number>)
之类的东西,其中所有类型都是相同的,并且没有最大的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);
这不会引起任何错误,我认为对于开发人员也很容易阅读。
更好的方法
在另一个答案中,帕夫洛提供了我比自己更喜欢的另一个解决方案。
type Foo =
& ((string | number) => void)
& ((string, number) => void)
const foo: Foo = (name, age) => {...};
它以更干净的方式解决了相同的问题,从而使您更加灵活。通过创建多种函数类型的交集,您可以描述每种不同的调用函数的方式,从而可以根据调用函数的方式尝试每个函数。
您可以通过使用&
加入来定义多个功能签名:
type Foo =
& ((string | number) => void)
& ((string, number) => void)
尝试。
您进行的三种可能的锻炼中,我已经弄清楚了如何使用单个选项对象使其正常工作,但是因为您需要设置至少一个对象,因此您需要定义每种可能性。
这样:
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({});