[PR] 本記事にはプロモーションが含まれています。

【TypeScript】タプル(Tuple)とは?配列との違いと使いどころ、readonly・as const・ラベル付きタプルまで

TypeScript

TypeScriptを勉強していると、「タプル(Tuple)」という言葉が出てきます。見た目は配列と同じ [ ] なので、最初は違いが分かりにくいところです。

C言語でいえば、「メンバー名のない構造体」に近いものです。この記事では、タプルと配列の違い、使いどころ、実際に使うときにつまずきやすい点を、動くコードで確かめます。コードはTypeScript Playgroundに貼れば、そのまま試せます。

タプルとは:仕切りの決まったお弁当箱

タプルは、「要素の数と、何番目にどの型が入るかが決まった配列」です。身近なものにたとえると、次のようになります。

  • 配列:
    バイキングのお皿。好きなものを、好きな順番で、いくつでも載せられる
  • タプル:
    仕切りの決まったお弁当箱。1つ目の仕切りにはご飯、2つ目にはおかず、のように入れる場所と数が決まっている

コードで見る配列とタプルの違い

配列

TypeScript
// 文字列と数値が混ざった配列
const arr1: (string | number)[] = ["田中", 25];

// 順番を逆にしてもエラーにならない
const arr2: (string | number)[] = [25, "田中"];

// 要素をいくつでも追加できる
arr1.push("東京");

(string | number)[] は「文字列か数値が入る配列」という意味です。入る型は決められますが、順番や要素の数までは決められません。

タプル

TypeScript
// 1番目は string(名前)、2番目は number(年齢)
const tuple1: [string, number] = ["田中", 25];

// 順番を逆にするとエラー
const tuple2: [string, number] = [25, "田中"];

// 要素の数が違うとエラー
const tuple3: [string, number] = ["田中", 25, "東京"];

// ない番号を読もうとするとエラー
console.log(tuple1[2]);

Playgroundでは、次のようなエラーが出ます(一部を抜粋)。

Plaintext
Type 'number' is not assignable to type 'string'.
Type '[string, number, string]' is not assignable to type '[string, number]'.
  Source has 3 element(s) but target allows only 2.
Tuple type '[string, number]' of length '2' has no element at index '2'.

上から順に「1番目に数値は入れられない」「要素が3つあるが2つまで」「番号2の要素はない」という意味です。[string, number] と書くだけで、数と順番の間違いをコンパイルのときに見つけられます。

なお、タプルは TypeScript の型の上だけの区別です。JavaScriptに変換されて実行されるときは、ただの配列になります。

タプルの使いどころ

名前と年齢のようなデータは、ふつうは { userName: "田中", age: 25 } のようなオブジェクトで表します。プロパティ名があるほうが、あとで読んだときに意味が分かるからです。タプルが向いているのは、次のような場面です。

① 関数から複数の値を返して、受け取る側で名前を付けたいとき

Reactの useState は、この形の代表例です。

TypeScript
// useState の戻り値は、[今の値, 値を更新する関数] というタプル
const [count, setCount] = useState(0);

タプルで返すと、受け取る側が [count, setCount] のように好きな名前で取り出せます。同じ画面で useState を何度も使うときも、[userName, setUserName] のようにそれぞれ名前を付けられるのが便利です。[a, b] = ... の取り出し方(分割代入)は、「文字列のデータをオブジェクトの配列に変える処理」でも使っています。

② 座標のように、順番だけで意味が分かる短い組

TypeScript
// [x座標, y座標]
const point: [number, number] = [10, 20];

// [緯度, 経度](東京駅のあたり)
const tokyoStation: [number, number] = [35.6812, 139.7671];

「x、yの順」のように、誰が見ても順番が分かる短い組なら、タプルでも読みやすさは落ちません。逆に、要素が3つを超えたり、順番を覚えないと意味が分からなかったりするときは、オブジェクトにしたほうが安全です。

つまずきやすいこと

型を書かないと、タプルではなく配列になる

関数で ["田中", 25] のように返しても、戻り値の型を書かないと、TypeScriptはタプルではなく「文字列か数値の配列」として扱います。

TypeScript
function getUser() {
  return ["田中", 25]; // 型は (string | number)[] になる
}

const [userName, age] = getUser();
// userName も age も、string | number 型になってしまう

age.toFixed(1); // エラー:age が文字列かもしれないので toFixed を呼べない

タプルとして扱わせるには、戻り値の型を書くか、as const を付けます。

TypeScript
// 方法1:戻り値の型を書く
function getUser1(): [string, number] {
  return ["田中", 25];
}

// 方法2:as const を付ける(readonly ["田中", 25] という型になる)
function getUser2() {
  return ["田中", 25] as const;
}

const [name1, age1] = getUser1();
age1.toFixed(1); // OK:age1 は number 型

as const を付けると、中身もその値そのもの("田中" や 25)の型になり、書き換えもできなくなります。途中で値を変える予定があるなら、方法1のように型を書くほうが扱いやすいです。

ふつうのタプルは push できてしまう

要素の数が決まっているはずのタプルでも、push はエラーになりません。変更されたくないときは、readonly を付けます。

TypeScript
const pair: [number, number] = [10, 20];
pair.push(30); // エラーにならない(実行すると要素が3つになる)

const fixedPair: readonly [number, number] = [10, 20];
fixedPair.push(30); // エラー:push は使えない
fixedPair[0] = 5;   // エラー:書き換えもできない

知っておくと便利な書き方

ラベル付きタプル

それぞれの要素に名前(ラベル)を付けられます。動きは変わりませんが、エディタで型を確かめたときにラベルが表示されるので、何番目が何か分かりやすくなります。

TypeScript
type LatLng = [lat: number, lng: number];

const osakaStation: LatLng = [34.7025, 135.4959];

省略できる要素

? を付けると、その要素は省略できます。省略できるのは、後ろのほうの要素だけです。

TypeScript
// Range という名前はブラウザの型とぶつかるので、NumberRange にしている
type NumberRange = [start: number, end?: number];

const r1: NumberRange = [1, 10]; // OK
const r2: NumberRange = [1];     // OK:end を省略

まとめ

  • タプルは、要素の数と順番ごとの型が決まった配列。実行するときはただの配列
  • 配列は「同じ種類のものをいくつでも」、タプルは「決まった形の少ない組」に向いている
  • 意味が分かりにくくなるときは、プロパティ名のあるオブジェクトを使う
  • 関数から返すときは、戻り値の型を書くか as const を付けないと配列になる
  • 変更させたくないときは readonly、意味を分かりやすくしたいときはラベル付きタプル

関数の書き方(=>)は「アロー関数(=>)とは?」で紹介しています。

ほかの開発・プログラミングの記事は、開発・プログラミングメモの目次から、テーマ別に探せます。

このブログは、にほんブログ村と人気ブログランキングに参加しています。記事が役に立ったら、下のランキングのバナーのどれかを押して応援してもらえると励みになります。

ブログランキング・にほんブログ村へにほんブログ村

サーバー構築・運用ランキング プログラムランキング

TypeScript
watasoftdevをフォローする

コメント