【入門編】関数引数における「Indexed Access Types」を用いた、特定のプロパティ型のみの抽出 – TypeScript コア・型システムの基礎解析バイブル

こんにちは!TypeScriptの型システムの世界へようこそ。
フロントエンドからバックエンドまで、コードの「安全の網」を張り巡らせるTypeScriptですが、型を学び始めた頃って「オブジェクトの型定義をしたはいいけれど、関数の引数にする時にどう指定すればいいんだろう?」と手が止まってしまうこと、ありますよね。

今回は、巨大な設定オブジェクト全体をベタッと関数に渡すのではなく、「そのオブジェクトの一部のプロパティの型だけを華麗に抜き出して引数にする」という、実務でめちゃくちゃ使えるテクニック「Indexed Access Types(インデックスアクセス型)」を一緒にマスターしていきましょう!

ここをクリアすれば、コンポーネントや関数がまとう「結合度(お互いの結びつきの強さ)」をフワッと軽くできるようになり、TypeScriptの基本はバッチリマスターできたと言って過readyStateになりますよ。ぜひ最後までついてきてくださいね。

—

巨大な設定オブジェクトの「ある悩み」

例えば、アプリケーション全体の様々な設定情報を保持する、こんな巨大なオブジェクト型があったとします。

type AppConfig = {
theme: ‘light’ | ‘dark’;
apiEndpoint: string;
timeout: number;
retryCount: number;
enableCache: boolean;
};

開発を進めていると、「この関数の仕事は、タイムアウトの設定値だけを受け取って処理をすることだな」という場面に出くわします。そんな時、あなたならどうやって関数の型を定義しますか?

❌ やりがちなアプローチ:バラバラの型を乱立させる

もしかすると、こんな風に書きたくなってしまうかもしれません。

// タイムアウト専用の型をわざわざ別で作る
type TimeoutSetting = number;

function handleTimeout(timeout: TimeoutSetting) {
console.log(`Timeout is set to ${timeout}ms`);
}

これ、一見すると動くので問題なさそうに見えますが、「Single Source of Truth(真実の情報源は一つ)」という原則に反するため、のちのち悲劇を生みます。
もし後日、仕様変更で `timeout` の型を「秒を表す文字列(`string`)」に変更することになったらどうでしょう? `AppConfig` を直すだけではなく、あちこちに散らばったバラバラの型を手作業で修正しなければならなくなります。これはメンテナンスの悪夢です。

—

解決策:Indexed Access Types で「型をピンポイントで引っこ抜く」

ここで登場するのが、TypeScriptの強力な機能である Indexed Access Types(インデックスアクセス型) です。

JavaScriptでオブジェクトの値を取り出すとき、 `config[‘timeout’]` のように書きますよね。あれと全く同じ感覚で、「型から特定のプロパティの型だけを抜き出す」ことができるんです。

書き方はこうです。

// [元の型の名前][ ‘プロパティ名’ ]
type ExtractedTimeout = AppConfig[‘timeout’]; // 結果: number

これを使えば、`AppConfig` という大元(真実のソース)を一切汚さず、かつ重複定義をせずに、特定のプロパティ型だけを手に入れることができます。

実際に書いてみましょう

先ほどの `AppConfig` を使った実用的なコードを見てみましょう。

type AppConfig = {
theme: ‘light’ | ‘dark’;
apiEndpoint: string;
timeout: number;
retryCount: number;
enableCache: boolean;
};

// 1. AppConfig から ‘timeout’ プロパティの型だけを抽出する
type TimeoutType = AppConfig[‘timeout’];

// 2. 抽出した型をそのまま関数の引数に使う
function applyTimeout(timeout: TimeoutType): void {
console.log(`タイムアウト設定を ${timeout} ミリ秒に適用します。`);
}

// 実行例
const config: AppConfig = {
theme: ‘dark’,
apiEndpoint: ‘https://api.example.com’,
timeout: 5000,
retryCount: 3,
enableCache: true,
};

// 正しく型が推論・チェックされて渡せます
applyTimeout(config.timeout); // 出力: タイムアウト設定を 5000 ミリ秒に適用します。

めちゃくちゃスマートだと思いませんか?
さらに素晴らしいのは、もし `AppConfig` の `timeout` が `number | string` に変更された場合、`applyTimeout` の引数型も自動的に追従して変わるという点です。型の一貫性が完全に保たれるんです。

—

応用編:複数まとめ取りや、配列・ユニオン型との組み合わせ

インデックスアクセス型は、一つのプロパティだけでなく、さらに応用が効きます。

1. 複数のプロパティを一度に抽出する(ユニオン型)

もし2つのプロパティのどちらかを受け取る関数を作りたい場合は、JavaScriptの配列アクセスのノリで、型をユニオン(`|`)で繋ぐことができます。

// theme と apiEndpoint の型を両方欲しい!
type ThemeOrEndpoint = AppConfig[‘theme’] | AppConfig[‘apiEndpoint’];
// 結果: (‘light’ | ‘dark’) | string

2. 配列の要素の型を抜き出す (`number` インデックス)

これはおまけの知見ですが、配列やタプル型に対してもインデックスアクセスは使えます。実務では `typeof` と組み合わせてよく使われます。

const roles = [‘admin’, ‘editor’, ‘viewer’] as const;

// 配列の要素全体の型をユニオンとして一発で抜き出す
type Role = typeof roles[number]; // ‘admin’ | ‘editor’ | ‘viewer’

—

陥りやすい文法エラーと注意ポイント

TypeScriptを学び始めたエンジニアが、インデックスアクセス型を使う際によくハマるポイントをいくつかご紹介しておきますね。

⚠️ 注意1:存在しないプロパティ名を指定している

JavaScriptでは、存在しないプロパティにアクセスすると `undefined` が返ってきますが、TypeScriptの型安全の世界では許されません。

type BadType = AppConfig[‘notExistProperty’];
// ❌ エラー: Type ‘notExistProperty’ does not exist on type ‘AppConfig’.

スペルミスがないか、キーが確実に存在するかをエディターがビシッと教えてくれます。

⚠️ 注意2:値ではなく「型」に対して指定している

ここが初心者が一番勘違いしやすいところです。インデックスアクセスは「型(Type)」に対して行います。実行時の「値(Value)」に対して使おうとするとエラーになります。

const myConfig: AppConfig = { / … / };

// ❌ エラー: 値に対して ‘[]’ は使えない(JavaScriptのプロパティアクセスになってしまう)
// type ErrorType = myConfig[‘timeout’];

// ⭕️ 正解: 必ず「型名」に対して行う
type CorrectType = AppConfig[‘timeout’];

もし値から型を引っ張りたい場合は、おなじみの `typeof` 演算子を挟んで `typeof myConfig[‘timeout’]` と書く必要があります。

—

まとめ

いかがでしたか?

今回は、関数引数の型定義を劇的にスッキリさせ、疎結合なコードを生み出す Indexed Access Types について解説しました。

  • 巨大な設定オブジェクトを丸ごと渡すのではなく、必要なパーツだけを `AppConfig[‘propertyName’]` で切り出す。
  • 大元を「真実のソース」として一元管理できるため、仕様変更に強いコードになる。
  • あくまで「型」に対して適用する(値に直接使いたいときは `typeof` を併用する)。

このテクニックを身につけると、コンポーネントや関数の再利用性がグッと上がり、大規模なTypeScriptコードでも迷子にならなくなります。
「ここをクリアすれば、TypeScriptの基本はバッチリマスターできますよ!」の言葉通り、ぜひ明日の開発からコードに取り入れてみてくださいね。あなたのTypeScriptライフがより快適なものになりますように!

タイトルとURLをコピーしました