関数
関数は、パラメーター(入力)と戻り値(出力)の2か所に型が適用されます。
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`
関数の構文
それぞれわずかに異なる構文を持つ3つの形式の関数があります。
関数宣言
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では、関数の parameter listの先頭に特別なパラメーターを追加することで、このコンテキストの型に注釈を付けることができます。
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`パラメーターは常に parameter listの最初に表示され、注釈が必要です。さらに、アロー関数には`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]
これは、`?string`ではなく`string`としての`a`と`b`の絞り込み情報が、`truthy`関数から戻るときに失われるためです。
次のように、`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([]);
もう1つの制限は、エンコードできる述語の範囲です。述語関数でサポートされている絞り込みは、それぞれの呼び出しに引数として渡される値を直接参照する必要があります。
たとえば、*インライン*絞り込みを考えてみましょう。
1declare const obj: {n?: number};2
3if (obj.n != null) {4 const n: number = obj.n;5}
ここでは、Flowでは`obj.n`を`?number`から`number`に絞り込むことができます。ここでの絞り込みは、`obj`自体ではなく、`obj`のプロパティ`n`であることに注意してください。
同じ条件をエンコードする*述語*関数を作成しようとすると、次の絞り込みは失敗します。
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`のエイリアスにすぎず、安全ではありません。不明瞭な型のリントを使用して、コードでの使用を禁止できます。