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

こんにちは!TypeScriptの世界へようこそ。
フロントエンドからバックエンドまで、型システムの恩恵を最大限に受けて「絶対に壊れないコード」を書く楽しさを一緒に味わっていきましょう。

今回は、TypeScriptの型システムの中でも、特に実務で「おっ、これスマートだな」と唸らせるテクニックである「Indexed Access Types(インデックスアクセス型)」を用いた関数引数の設計について解説します。

「巨大な設定オブジェクトやAPIのレスポンス型があるけれど、関数に渡したいのはそのうちのたった1つのプロパティだけなんだよな……」そんなもどかしさを感じたことはありませんか?
コピペや場当たり的な型定義でDRY原則(Don’t Repeat Yourself)を破る前に、ここをクリアしてTypeScriptマスターへの階段を一段登りましょう!

—

1. 現場でよくある「モヤモヤ」:巨大な型と一部の引数

例えば、ユーザーのプロフィール情報、設定、メタデータなどがギュッと詰まった、次のような巨大なインターフェース(設計図)があったとします。

interface UserProfile {
id: string;
username: string;
email: string;
settings: {
theme: ‘light’ | ‘dark’;
notifications: boolean;
language: string;
};
metadata: {
lastLogin: Date;
loginCount: number;
};
}

さて、このアプリの中で「ユーザーのテーマ設定(`theme`)だけを受け取って、画面のCSSを切り替える関数」を作りたいとします。

初学者の方がやりがちなのは、このようなアプローチです。

// ❌ やりがちなアプローチ:専用の型をもう一つ作ってしまう
type ThemeType = ‘light’ | ‘dark’;

function applyTheme(theme: ThemeType) {
// テーマを適用する処理
}

一見すると動きますが、もし将来 `UserProfile[‘settings’][‘theme’]` に `’system’`(OS設定に従う)という新しい選択肢が追加されたらどうなるでしょう?
`UserProfile` を直したあとに、わざわざ `ThemeType` も手動で書き直さなければなりませんよね。これでは「型の二重管理(DRY原則の違反)」になり、修正漏れのバグを生む温床になってしまいます。

—

2. 解決策:Indexed Access Types で「型の部分借用」をする

ここで登場するのが、TypeScriptの Indexed Access Types(インデックスアクセス型) です。
JavaScriptでオブジェクトのプロパティにアクセスするとき、`obj.prop` や `obj[‘prop’]` と書きますよね。あれの「型バージョン」だと思ってください。

「`UserProfile` 型の中にある `settings` プロパティの型をちょうだい」「さらにその中の `theme` プロパティの型をちょうだい」という指定を、型空間の中で行えます。

先ほどの関数を、インデックスアクセス型を使って書き直してみましょう。

// ⭕️ インデックスアクセス型を使ったスマートなアプローチ
function applyTheme(theme: UserProfile[‘settings’][‘theme’]) {
console.log(`Applying theme: ${theme}`);
}

// 使い方
applyTheme(‘dark’); // OK
applyTheme(‘light’); // OK
// applyTheme(‘blue’); // ❌ コンパイルエラー!’blue’ は指定できません

このコードの仕組み

`UserProfile[‘settings’][‘theme’]` という記述は、コンパイル時に TypeScript が次のように解釈(評価)しています。

1. `UserProfile` の中から `settings` の型を取り出す:`{ theme: ‘light’ | ‘dark’; … }`
2. その中からさらに `theme` の型を取り出す:`’light’ | ‘dark’`

つまり、関数 `applyTheme` の引数の型は、常に大元の `UserProfile` と同期された状態になります。大元が変われば、この関数の型も自動的に追従するのです。これが「DRY原則を守る型設計」の本質です。

—

3. もう一歩進んだ実用パターン:オブジェクト全体を渡すのではなく「一部」だけを渡す関数

実務では、プリミティブ値(文字列や数値)だけでなく、オブジェクトの一部をそのまま別のコンポーネントやヘルパー関数に渡したいケースも多々あります。

たとえば、「ユーザーのメタデータ部分だけ」を更新する関数を作ってみましょう。

// UserProfile の metadata プロパティの型だけを抽出して引数にする
function updateMetadata(metadata: UserProfile[‘metadata’]) {
console.log(`Last login was at: ${metadata.lastLogin}`);
}

const currentUser: UserProfile = {
id: ‘usr_001’,
username: ‘Taro’,
email: ‘taro@example.com’,
settings: {
theme: ‘dark’,
notifications: true,
language: ‘ja’,
},
metadata: {
lastLogin: new Date(),
loginCount: 42,
},
};

// 大元のオブジェクトから該当部分を切り出して渡す
updateMetadata(currentUser.metadata);

ここで「あれ、わざわざ `UserProfile[‘metadata’]` って書かずに、`typeof currentUser.metadata` でも良くない?」と思ったそこのあなた!鋭いですね。
変数の値から型を導出する `typeof` 演算子も非常に強力ですが、「関数やコンポーネントのシグネチャ(設計図)を、値に依存せず、型定義(Interface)ベースで美しく保ちたい」というアーキテクチャの観点からは、Indexed Access Types が圧倒的に有利な場面が多くあります。

—

4. 陥りやすい文法エラーと注意点

初心者の開発者がインデックスアクセス型を使う際によくやってしまうミスをいくつかご紹介します。ここを知っておくだけで、無駄なエラーに悩まされずに済みますよ。

① プロパティ名を間違える(タイポ)

JavaScriptと同じく、存在しないプロパティ名を指定すると、当然ながらコンパイルエラーになります。

// ❌ 存在しないプロパティ名を指定した場合
type BadType = UserProfile[‘setting’]; // ‘setting’ ではなく ‘settings’
// ➡️ エラー: Property ‘setting’ does not exist on type ‘UserProfile’.

TypeScriptのコンパイラが正しいプロパティ名をサジェストしてくれるので、エディタの補完機能をしっかり使いましょう。

② 配列の要素の型を取る(`number` または `[number]` の活用)

もし `UserProfile` の中に「お気に入りの記事一覧」のような配列があった場合、その「要素1つ分の型」はどうやって取ればよいでしょうか?
ここで配列特有のインデックスアクセスが登場します。

interface ArticleList {
posts: {
id: string;
title: string;
}[];
}

// 配列の要素の型を取り出すときは `[number]` を使う!
type SinglePost = ArticleList[‘posts’][number];

function renderPost(post: SinglePost) {
console.log(post.title);
}

`[‘posts’][number]` と書くことで、「`posts` 配列の中にある要素のいずれかの型」を綺麗に抽出できます。これも実務でめちゃくちゃよく使うテクニックなので、ぜひ覚えておいてくださいね。

—

まとめ:型を「つなぐ」意識を持とう

今回は、関数引数における Indexed Access Types を用いたプロパティ型の抽出について解説しました。

  • 大元の型が変わっても、連動して型が自動追従する(DRY原則の守護神)
  • `InterfaceName[‘propName’]` のように、JavaScriptのプロパティアクセスと同じ感覚で型を取り出せる
  • 配列の場合は `[number]` を組み合わせることで要素の型を抽出できる

「型を新しく手打ちで作るな、既存の型から賢く引き出せ」。これが中級者、そしてシニアエンジニアへの扉を開く合言葉です。

ここをクリアできれば、あなたの書くTypeScriptコードはより堅牢で、メンテナンス性の高い美しい芸術品へと進化します。
日々のコーディングで「あ、この型、あそこから流用できないかな?」というアンテナをぜひ張ってみてくださいね。

それでは、次のレクチャーでお会いしましょう!バッチリマスターしていきましょう!

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