タプルの要素アクセスは「位置ごとに型が決まっている」のがキモ
タプルは、「何番目に何の型が入るか」まで型で決まっている配列でした。
だからこそ、要素にアクセスしたときも「どの位置を取るか」で型が変わります。
let user: [string, number] = ["Taro", 20];
const name = user[0]; // string
const age = user[1]; // number
TypeScriptここで TypeScript は、user[0] は必ず string、user[1] は必ず number だと分かっています。
「位置ごとに型が違う」ことが、そのままアクセス時の型に反映される——これがタプルの要素アクセスの一番大事なポイントです。
インデックスアクセスの基本:配列と同じ書き方、でも結果の型が違う
0番目・1番目…と普通にアクセスする
書き方自体は、普通の配列とまったく同じです。
let point: [number, number] = [10, 20];
const x = point[0]; // number
const y = point[1]; // number
TypeScriptここではたまたま両方 number なので配列とあまり違いが見えませんが、
型としては「0番目は number」「1番目も number」と位置ごとに決まっています。
位置ごとに型が違うタプルだと、差がはっきり見える
let log: [string, number, boolean] = ["login", 200, true];
const event = log[0]; // string
const status = log[1]; // number
const success = log[2]; // boolean
TypeScript普通の配列なら log[0] も log[1] も同じ型ですが、
タプルは「位置ごとに型が違う」ので、アクセスしたときの型もそれぞれ違います。
ここで「タプルは“位置付きの型”なんだ」という感覚が一気に掴めます。
存在しない位置へのアクセスはコンパイルエラーになる
タプルは「長さ」も型の一部
let user: [string, number] = ["Taro", 20];
// const x = user[2]; // エラー:タプルの長さは2
TypeScript配列なら user[2] は undefined になるだけですが、
タプルは「要素数も型に含まれている」ので、存在しない位置へのアクセスはコンパイルエラーになります。
これはかなり強力で、「本来ありえない位置」をコード上で触ろうとした瞬間に止めてくれます。
「このタプルは2つで1セット」「このタプルは3つで1セット」という前提を、TypeScript がちゃんと守らせてくれるわけです。
分割代入での要素アクセス(タプルと相性がいい)
位置に意味があるなら、分割代入で名前を付ける
タプルは「位置に意味がある」ので、その意味を変数名に落とし込むと読みやすくなります。
let user: [string, number] = ["Taro", 20];
const [name, age] = user;
name; // string
age; // number
TypeScriptuser[0], user[1] と書くよりも、
「これは name」「これは age」と名前を付けた方が、コードを読む人にとっても親切です。
関数の戻り値タプルとの組み合わせ
function splitName(fullName: string): [string, string] {
const [first, last] = fullName.split(" ");
return [first, last];
}
const [first, last] = splitName("Taro Yamada");
// first: string
// last: string
TypeScript「タプルで返す → 分割代入で受け取る」というパターンは、
タプルの要素アクセスの一番気持ちいい使い方のひとつです。
タプルの要素アクセスと型推論の注意点
型注釈を書かないと「ただの混ざった配列」になる
const user = ["Taro", 20];
// 型: (string | number)[]
TypeScriptこの場合、user[0] も user[1] も string | number です。
const a = user[0]; // string | number
const b = user[1]; // string | number
TypeScript「位置ごとに型が違う」という情報は失われてしまいます。
タプルとして扱いたいなら、型注釈か as const が必要です。
タプルとして扱うとアクセス時の型がハッキリする
const user1: [string, number] = ["Taro", 20];
const name1 = user1[0]; // string
const age1 = user1[1]; // number
TypeScriptまたは、
const user2 = ["Taro", 20] as const;
// 型: readonly ["Taro", 20]
const name2 = user2[0]; // "Taro"
const age2 = user2[1]; // 20
TypeScriptas const を使うと、リテラル値まで型に固定されます(ここでは "Taro" と 20)。
「この並びそのものが仕様」というときには、かなり強い表現になります。
どんなときに「タプルの要素アクセス」を意識するといいか
タプルの要素アクセスが本領を発揮するのは、
「この2つ(3つ)は、順番と意味が決まった1セットだ」と感じる場面です。
座標([x, y])
範囲([start, end])
サイズ([width, height])
関数の戻り値のペア([result, error] など)
こういうときにタプルを使っておくと、value[0] や value[1] にアクセスした瞬間に、TypeScript が「ここはこの型だよ」と教えてくれます。
「配列の要素アクセス」は「どこを取っても同じ型」
「タプルの要素アクセス」は「どこを取るかで型が変わる」
この違いが腑に落ちると、
「これは配列でいいな」「これはタプルにした方がいいな」という判断が、かなりクリアになってきます。
