如何在流中使用多个可能的呼叫签名来注释函数



在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({});

最新更新