跳至主要内容

函数

函数有两个地方应用类型:参数(输入)和返回值(输出)。

1function concat(a: string, b: string): string {2  return a + b;3}4
5concat("foo", "bar"); // Works!6concat(true, false);  // Error!
6:8-6:11: Cannot call `concat` with `true` bound to `a` because boolean [1] is incompatible with string [2]. [incompatible-call]
6:14-6:18: Cannot call `concat` with `false` bound to `b` because boolean [1] is incompatible with string [2]. [incompatible-call]

使用推断,返回值通常是可选的

1function concat(a: string, b: string) {2  return a + b;3}4
5const s: string = concat("foo", "bar"); // Works!

如果可以在表达式上下文中获取类型,则类型注解可以是可选的

1[1, 2, 3].map(x => x * x); // From the context, we know parameter `x` has type `number`

函数语法

有三种形式的函数,每种都有略微不同的语法。

函数声明

1function func(str: string, bool?: boolean, ...nums: Array<number>): void {2  // ...3}

箭头函数

1let func = (str: string, bool?: boolean, ...nums: Array<number>): void => {2  // ...3};

函数类型

1type T = (str: string, bool?: boolean, ...nums: Array<number>) => void;

您也可以选择省略参数名称。

1type T = (string, boolean | void, Array<number>) => void;

您可能会将这些函数类型用于回调等。

1function func(callback: (error: Error | null, value: string | null) => void) {2  // ...3}

类型参数

函数可以有类型参数

1function f<T>(x: T): Array<T> {2  return [x];3}4
5const g = <T>(x: T): Array<T> => [x];6
7type H = <T>(T) => Array<T>;

函数参数

函数参数可以通过在参数名称后添加冒号 :,然后添加类型来指定类型。

1function func(param1: string, param2: boolean) {2  // ...3}

可选参数

您还可以通过在参数名称后添加问号 ?,然后添加冒号 : 来指定可选参数。

1function func(optionalValue?: string) {2  // ...3}

可选参数将接受缺失、undefined 或匹配的类型。但它们不会接受 null

1function func(optionalValue?: string) {2  // ...3}4
5func();          // Works.6func(undefined); // Works.7func("string");  // Works.8
9func(null);      // Error!
9:6-9:9: Cannot call `func` with `null` bound to `optionalValue` because null [1] is incompatible with string [2]. [incompatible-call]

剩余参数

JavaScript 还支持使用剩余参数或在参数列表末尾收集参数数组的参数。这些参数在前面有一个省略号 ...

您还可以使用相同的语法为剩余参数添加类型注解,但使用 Array

1function func(...args: Array<number>) {2  // ...3}

您可以将任意数量的参数传递给剩余参数。

1function func(...args: Array<number>) {2  // ...3}4
5func();        // Works.6func(1);       // Works.7func(1, 2);    // Works.8func(1, 2, 3); // Works.

注意:如果您为剩余参数添加类型注解,它必须始终显式地为 Array$ReadOnlyArray 类型。

this 参数

JavaScript 中的每个函数都可以使用名为 this 的特殊上下文调用。您可以使用您想要的任何上下文调用函数。Flow 允许您通过在函数参数列表的开头添加一个特殊参数来为该上下文添加类型注解

1function func<T>(this: { x: T }) : T {2  return this.x;3}4
5const num: number = func.call({x : 42});6const str: string = func.call({x : 42}); // Error!
6:21-6:39: Cannot assign `func.call(...)` to `str` because number [1] is incompatible with string [2]. [incompatible-type]

此参数在运行时没有效果,并且在 Flow 转换为 JavaScript 时与类型一起被删除。如果存在,this 参数必须始终出现在函数参数列表的最开头,并且必须有注解。此外,箭头函数 可能没有 this 参数注解,因为这些函数在定义位置绑定它们的 this 参数,而不是在调用位置。

如果未提供显式的 this 参数,Flow 将尝试根据使用情况推断一个。如果在函数主体中未提及 this,Flow 将为其 this 参数推断 mixed

函数返回值

函数返回值也可以通过在参数列表后添加冒号 :,然后添加类型来添加类型。

1function func(): number {2  return 1;3}

返回值类型确保函数的每个分支都返回相同的类型。这可以防止您在某些情况下意外地不返回值。

1function func(): boolean {
2 if (Math.random() > 0.5) {3 return true;4 }5}
1:18-1:24: Cannot expect boolean as the return type of function because boolean [1] is incompatible with implicitly-returned undefined. [incompatible-return]

异步函数隐式地返回一个 Promise,因此返回值类型必须始终为 Promise

1async function func(): Promise<number> {2  return 123;3}

谓词函数

有时您希望将 if 语句中的条件移到函数中

1function concat(a: ?string, b: ?string): string {2  if (a != null && b != null) {3    return a + b;4  }5  return '';6}

但是,Flow 将在下面的代码中报错

1function truthy(a: ?string, b: ?string): boolean {2  return a != null && b != null;3}4
5function concat(a: ?string, b: ?string): string {6  if (truthy(a, b)) {7    return a + b; // Error!
8 }9 return '';10}
7:12-7:16: Cannot use operator `+` with operands null or undefined [1] and null or undefined [2] [unsafe-addition]
7:12-7:16: Cannot use operator `+` with operands null or undefined [1] and string [2] [unsafe-addition]
7:12-7:16: Cannot use operator `+` with operands string [1] and null or undefined [2] [unsafe-addition]

这是因为当从 truthy 函数返回时,ab 的细化信息(为 string 而不是 ?string)丢失了。

您可以通过将 truthy 设为谓词函数来解决此问题,方法是使用 %checks 注解,如下所示

1function truthy(a: ?string, b: ?string): boolean %checks {2  return a != null && b != null;3}4
5function concat(a: ?string, b: ?string): string {6  if (truthy(a, b)) {7    return a + b;8  }9  return '';10}

谓词函数的局限性

这些谓词函数的主体需要是表达式(即不支持局部变量声明)。但是,可以在谓词函数内调用其他谓词函数。例如

1function isString(y: mixed): %checks {2  return typeof y === "string";3}4
5function isNumber(y: mixed): %checks {6  return typeof y === "number";7}8
9function isNumberOrString(y: mixed): %checks {10  return isString(y) || isNumber(y);11}12
13function foo(x: string | number | Array<mixed>): string | number {14  if (isNumberOrString(x)) {15    return x + x;16  } else {17    return x.length; // no error, because Flow infers that x can only be an array18  }19}20
21foo('a');22foo(5);23foo([]);

另一个局限性是可编码的谓词范围。谓词函数中支持的细化必须直接引用作为参数传递给相应调用的值。

例如,考虑内联细化

1declare const obj: {n?: number};2
3if (obj.n != null) {4  const n: number = obj.n;5}

在这里,Flow 将允许您将 obj.n?number 细化为 number。请注意,这里的细化是在 obj 的属性 n 上,而不是 obj 本身。

如果您尝试创建一个谓词函数来编码相同的条件,那么以下细化将失败

1function bar(a: {n?: number, ...}): %checks {2  return a.n != null;3}4
5declare const obj: {n?: number};6
7if (bar(obj)) {8  const n: number = obj.n; // Error
9}
8:21-8:25: Cannot assign `obj.n` to `n` because undefined [1] is incompatible with number [2]. [incompatible-type]

这是因为通过 bar 支持的唯一细化是在 obj 本身上。

可调用对象

可调用对象可以被类型化,例如

1type CallableObj = {2  (number, number): number,3  bar: string,4  ...5};6
7function add(x: number, y: number) {8  return x + y;9}10
11add.bar = "hello world";12
13add as CallableObj;

一般来说,如果函数是函数声明,或者形式为 const f = () => ... 的简单变量声明,则可以为函数分配属性。属性必须以 f.prop = <expr>; 的格式分配,与函数定义在同一个语句列表中(即不是条件地)。

请注意,表示分配给函数的静态属性的对象是不精确的。

重载函数

您可以使用交叉类型来定义重载函数类型

1declare const fn:2  & ((x: 'string') => string)3  & ((x: 'number') => number)4
5const s: string = fn('string');6const n: number = fn('number');

任意函数

如果您想指定要允许任何函数,并且不关心它是什么,您可以使用此模式

1function useCallback<T: (...$ReadOnlyArray<empty>) => mixed>(2  callback: T,3  inputs: ?$ReadOnlyArray<mixed>,4): T {5  return callback;6}7useCallback((x: string) => true); // OK8useCallback((x: number) => [1]); // OK

您可以使用类型参数来捕获参数和返回值类型,以进行更复杂的转换

1function func<TArgs: $ReadOnlyArray<mixed>, TReturn>(2  callback: (...TArgs) => TReturn,3): (boolean, ...TArgs) => Array<TReturn> {4  return (b, ...args): Array<TReturn> => {5    if (b) {6      return [callback(...args)];7    } else {8      return [];9    }10  };11}12
13const f: (boolean, string, number) => Array<string> =14  func((x: string, y: number) => x.slice(y)); // OK

类型 Function 只是 any 的别名,是不安全的。您可以使用 unclear-type 代码风格检查 禁止在代码中使用它。