跳至主要內容

函式

函式有兩個套用型別的地方:參數(輸入)和回傳值(輸出)。

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]

Rest 參數

JavaScript 也支援 Rest 參數,或在參數清單尾端收集陣列參數的參數。這些參數之前會有省略號 ...

您也可以使用相同的語法,但加上 Array 來為 Rest 參數加入類型註解。

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

您可以傳遞任意數量的參數至 Rest 參數。

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

注意:如果您為 Rest 參數加入類型註解,它必須明確為 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

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 lint來禁止在你的程式碼中使用它。