メインコンテンツにスキップ

タプル

タプル型は、要素が異なる型を持つことができる固定長のリストを表します。これは、長さが不明で、すべての要素が同じ型を持つ配列型とは対照的です。

タプルの基本

JavaScriptの配列リテラル値は、タプル型と配列型の両方を作成するために使用できます。

1const arr: Array<number> = [1, 2, 3]; // As an array type2const tup: [number, number, number] = [1, 2, 3]; // As a tuple type

Flowでは、[type1, type2, type3]構文を使用してタプル型を作成できます。

1const tuple1: [number] = [1];2const tuple2: [number, boolean] = [1, true];3const tuple3: [number, boolean, string] = [1, true, "three"];

タプルから特定のインデックスで値を取得すると、そのインデックスの型が返されます。

1const tuple: [number, boolean, string] = [1, true, "three"];2
3const num: number = tuple[0]; // Works!4const bool: boolean = tuple[1]; // Works!5const str: string  = tuple[2]; // Works!

存在しないインデックスにアクセスしようとすると、インデックス範囲外エラーが発生します。

1const tuple: [number, boolean, string] = [1, true, "three"];2
3const none = tuple[3]; // Error!
3:14-3:21: Cannot get `tuple[3]` because tuple type [1] only has 3 elements, so index 3 is out of bounds. [invalid-tuple-index]

Flowがアクセスしようとしているインデックスを認識できない場合は、可能なすべての型が返されます。

1const tuple: [number, boolean, string] = [1, true, "three"];2
3function getItem(n: number) {4  const val: number | boolean | string = tuple[n];5  // ...6}

タプル内に新しい値を設定する場合、新しい値はそのインデックスの型と一致する必要があります。

1const tuple: [number, boolean, string] = [1, true, "three"];2
3tuple[0] = 2;     // Works!4tuple[1] = false; // Works!5tuple[2] = "foo"; // Works!6
7tuple[0] = "bar"; // Error!
8tuple[1] = 42; // Error!
9tuple[2] = false; // Error!
7:12-7:16: Cannot assign `"bar"` to `tuple[0]` because string [1] is incompatible with number [2]. [incompatible-type]
8:12-8:13: Cannot assign `42` to `tuple[1]` because number [1] is incompatible with boolean [2]. [incompatible-type]
9:12-9:16: Cannot assign `false` to `tuple[2]` because boolean [1] is incompatible with string [2]. [incompatible-type]

厳密に適用されるタプルの長さ(アリティ)

タプルの長さは「アリティ」として知られています。タプルの長さはFlowで厳密に適用されます。

これは、短いタプルを長いタプルの代わりに使用できないことを意味します。

1const tuple1: [number, boolean] = [1, true];2
3const tuple2: [number, boolean, void] = tuple1; // Error!
3:41-3:46: Cannot assign `tuple1` to `tuple2` because tuple type [1] has 2 elements but tuple type [2] has 3 elements. [invalid-tuple-arity]

また、長いタプルを短いタプルの代わりに使用することもできません。

1const tuple1: [number, boolean, void] = [1, true, undefined];2
3const tuple2: [number, boolean] = tuple1; // Error!
3:35-3:40: Cannot assign `tuple1` to `tuple2` because tuple type [1] has 3 elements but tuple type [2] has 2 elements. [invalid-tuple-arity]

オプションの要素は、アリティを範囲にします。

タプルは配列型と一致しない

Flowは配列の長さを認識しないため、Array<T>型をタプルに渡すことはできません。

1const array: Array<number> = [1, 2];2
3const tuple: [number, number] = array; // Error!
3:33-3:37: Cannot assign `array` to `tuple` because array type [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]

また、タプル型をArray<T>型に渡すことはできません。そうすると、安全でない方法でタプルを変更する可能性があるからです(たとえば、3番目の項目をpushするなど)。

1const tuple: [number, number] = [1, 2];2
3const array: Array<number> = tuple; // Error!
3:30-3:34: Cannot assign `tuple` to `array` because tuple type [1] is incompatible with array type [2]. [incompatible-type]

ただし、$ReadOnlyArray型には渡すことができます。これは、変更が許可されていないためです。

1const tuple: [number, number] = [1, 2];2
3const array: $ReadOnlyArray<number> = tuple; // Works!

タプルで配列を変化させるメソッドを使用できない

タプルを変更するArray.prototypeメソッドは使用できません。変更しないメソッドのみ使用できます。

1const tuple: [number, number] = [1, 2];2tuple.join(', '); // Works!3
4tuple.push(3); // Error!
4:7-4:10: Cannot call `tuple.push` because property `push` is missing in `$ReadOnlyArray` [1]. [prop-missing]

長さの絞り込み

長さによって、タプルのユニオンを絞り込むことができます。

1type Union = [number, string] | [boolean];2function f(x: Union) {3  if (x.length === 2) {4    // `x` is `[number, string]`5    const n: number = x[0]; // OK6    const s: string = x[1]; // OK7  } else {8    // `x` is `[boolean]`9    const b: boolean = x[0];10  }11}

タプル要素のラベル

注意:このセクションおよび以降のセクションでは、このページの最後にある「導入」セクションで説明されているように、ツールを更新する必要があります。

タプル要素にラベルを追加できます。このラベルはタプル要素の型には影響しませんが、特に複数の要素が同じ型を持つ場合に、タプル要素の目的を自己文書化するのに役立ちます。

1type Range = [x: number, y: number];

ラベルは、要素に分散アノテーションまたはオプション修飾子を追加するためにも必要です(ラベルがないと、解析のあいまいさが発生するため)。

分散アノテーションと読み取り専用タプル

オブジェクトのプロパティと同様に、ラベル付きタプル要素に分散アノテーション(読み取り専用/書き込み専用を示すため)を追加できます。

1type T = [+foo: number, -bar: string];

これにより、要素を読み取り専用または書き込み専用としてマークできます。 例えば

1function f(readOnlyTuple: [+foo: number, +bar: string]) {2  const n: number = readOnlyTuple[0]; // OK to read3  readOnlyTuple[1] = 1; // ERROR! Cannot write
4}
3:3-3:18: Cannot assign `1` to `readOnlyTuple[1]` because tuple element at index `1` [1] labeled `bar` is not writable. [cannot-write]
3:22-3:22: Cannot assign `1` to `readOnlyTuple[1]` because number [1] is incompatible with string [2]. [incompatible-type]

また、各プロパティを読み取り専用としてマークするためのショートカットとして、タプル型で$ReadOnlyを使用することもできます。

1type T = $ReadOnly<[number, string]>; // Same as `[+a: number, +b: string]`

オプションのタプル要素

要素のラベルの後に?を付けることで、タプル要素をオプションとしてマークできます。これにより、オプションの要素を省略できます。オプションの要素は、すべての必須要素の後、タプル型の最後に配置する必要があります。

1type T = [foo: number, bar?: string];2[1, "s"] as T; // OK: has all elements3[1] as T; // OK: skipping optional element

オプションの要素にundefinedを書き込むことはできません - そうする場合は、要素の型に| voidを追加してください。

1type T = [foo?: number, bar?: number | void];2declare const x: T;3x[0] = undefined; // ERROR
4[undefined] as T; // ERROR
5 6x[1] = undefined; // OK: we've added `| void` to the element type
3:8-3:16: Cannot assign `undefined` to `x[0]` because you cannot assign undefined [1] to optional tuple element [2] (to do so, add `| void` to the tuple element type). [incompatible-type]
4:2-4:10: Cannot cast array literal to `T` because you cannot assign undefined [1] to optional tuple element [2] (to do so, add `| void` to the tuple element type) in index 0. [incompatible-cast]

また、PartialおよびRequiredユーティリティ型を使用して、すべての要素をオプションまたは必須にすることもできます。

1type AllRequired = [number, string];2[] as Partial<AllRequired>; // OK: like `[a?: number, b?: string]` now3
4type AllOptional = [a?: number, b?: string];5[] as Required<AllOptional>; // ERROR: like `[a: number, b: string]` now
5:1-5:2: Cannot cast array literal to required of `AllOptional` because empty array literal [1] has 0 elements but `AllOptional` [2] has 2 elements. [invalid-tuple-arity]

オプションの要素を持つタプルは、単一の数値ではなく範囲であるアリティ(長さ)を持ちます。たとえば、[number, b?: string]の長さは1~2です。

タプルスプレッド

タプル型を別のタプル型にスプレッドして、より長いタプル型を作成できます。

1type A = [number, string];2type T = [...A, boolean]; // Same as `[number, string, boolean]`3[1, "s", true] as T; // OK

タプルスプレッドは、ラベル、分散、およびオプションを保持します。配列をタプルにスプレッドすることはできません。他のタプルのみをスプレッドできます。

値レベルでは、オプションの要素を持つタプルを配列リテラルにスプレッドした場合、そのスプレッドの後には何も持つことができず、配列値のタプルビューを保持できません。これは、オプションの要素を持つタプルは長さが範囲であるため、後続の値がどのインデックスになるかがわからないためです。この値は適切なArray<T>型として型付けできます。影響を受けるのは値のタプルビューのみです。

1const a: [foo?: 1] = [];2const b = [0, ...a, 2]; // At runtime this is `[0, 2]`3b as [0, 1 | void, 2]; // ERROR
4b as Array<number | void>; // OK5 6const c: [0, foo?: 1] = [0];7const d: [bar?: 2] = [2];8const e = [...c, ...d]; // At runtime this is `[0, 2]`9e as [0, foo?: 1, bar?: 2]; // ERROR
10e as Array<number | void>; // OK
3:1-3:1: Cannot cast `b` to tuple type because array literal [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]
9:1-9:1: Cannot cast `e` to tuple type because array literal [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]

導入

ラベル付きタプル要素(要素のオプション要素と分散アノテーションを含む)とタプルスプレッド要素を使用するには、構文をサポートするようにインフラストラクチャをアップグレードする必要があります。

  • flowおよびflow-parser:0.212.0
  • prettier: 3
  • babel-plugin-syntax-hermes-parserを含むbabel。セットアップ手順については、Babelガイドを参照してください。
  • hermes-eslintを含むeslint。セットアップ手順については、ESLintガイドを参照してください。