【実務・中級編】 typeof 型演算子 – TypeScript実践ガイド

お疲れ様です。最近、チームのコードレビューをしていて「あ、ここは `typeof` をうまく使えばもっとスマートになるのにな」と惜しいコードを見かけることが増えてきました。

中級へのステップアップの壁を越えようとしている君なら、`const` 宣言された値や、APIから返ってきたオブジェクトの構造をわざわざ手書きのインターフェースで再定義して、「あ、プロパティ名タイポしてた」なんて泥沼にハマった経験、一度や二度ではないはずだ。

今回は、そんな無駄なボイラープレート地獄から君を救い出し、型安全なフロントエンド開発を加速させる秘密兵器――`typeof` 型演算子について、実務の現場でどう使い倒すべきか、俺の知見をみっちり伝授しよう。

—

1. `typeof` 型演算子とは何か?(JavaScriptのそれとは違う)

まず最初に頭を切り替えてほしい。JavaScriptの `typeof` は、実行時に値のデータ型(`”string”`, `”number”`, `”object”` など)を文字列で返すランタイムの演算子だよね。

// JavaScriptの世界
console.log(typeof “hello”); // “string” と出力される

しかし、TypeScriptにおける `typeof`(型文脈での `typeof`)は、コンパイル時(型推論の世界)に、既存の値からその「型構造」を丸ごと引っこ抜くための演算子だ。ブラウザが実行するJavaScriptのコードには一切残りません。すべてTypeScriptのコンパイラ(tsc)が型チェックのために解釈し、ビルド時には綺麗に消え去る。ここが最大のポイントだ。

ブラウザの裏側はどうなっているのか?

ブラウザのJavaScriptエンジンは、実行時に型なんて見ていない(V8エンジンなどはJITコンパイル時に隠しクラスや型フィードバックを使うけど、それはTSの型とは別物だ)。TypeScriptの `typeof` は、あくまで開発時のエディタ(VSCodeなど)の補完と、tscによる静的解析を支えるためのメタデータ生成装置として機能している。

つまり、値の真実(Single Source of Truth)は常に「実装コード側の値」にあり、型側はそれに追従する。この「値ファースト」の設計思想を実現するために、`typeof` はなくてはならない存在なんだ。

—

2. 現場で使える!実践パターンとコード例

百聞は一見にしかず。実務のフロントエンド開発で「おっ、これ使えるな」と思える具体的なユースケースをいくつか見ていこう。そのままエディタに貼り付けて試せるように書いた。

パターンA: 設定オブジェクトや定数リストからの型抽出

現場によよくある、UIのステータス定義や、APIのエラーコード一覧などの定数オブジェクト。これをわざわざ `interface` で二重管理していませんか?

// 1. まずは定数オブジェクトを定義する(as const を忘れないのがプロの技)
export const USER_ROLES = {
ADMIN: ‘admin’,
EDITOR: ‘editor’,
VIEWER: ‘viewer’,
} as const;

// 2. typeof と keyof を組み合わせて、型を自動生成する
// これにより、値が変更されても型の書き換えが不要になる
export type UserRoleKey = keyof typeof USER_ROLES; // ‘ADMIN’ | ‘EDITOR’ | ‘VIEWER’
export type UserRoleValue = typeof USER_ROLES[UserRoleKey]; // ‘admin’ | ‘editor’ | ‘viewer’

// 実務での活用例
function handleUserAccess(role: UserRoleValue) {
if (role === ‘admin’) {
// 管理者向けの処理
console.log(‘権限レベル: 最高’);
} else {
// 一般向けの処理
console.log(‘権限レベル: 通常’);
}
}

// ちゃんと型安全に弾かれる
// handleUserAccess(‘super_user’); // エラー: 引数の型は ‘admin’ | ‘editor’ | ‘viewer’ です
handleUserAccess(USER_ROLES.ADMIN); // OK

ここで `as const`(const assertion)と一緒に使うのが鉄則だ。これがないと、単なる `string` 型にWidening(型幅の拡大)されてしまって、具体的なリテラル型が抽出できなくなってしまうから注意してくれ。

パターンB: APIレスポンスやモックデータからの型推論

バックエンドのAPI仕様書(Swagger等)がまだ固まっていない、あるいはモックファーストでフロントエンドを実装しなきゃいけない泥臭い状況ってあるよね。そんなときも `typeof` が救世主になる。

// フォームの初期値や、ダミーのAPIレスポンス定義
const initialFormState = {
username: ”,
age: 0,
isSubscribed: false,
tags: [] as string[], // 配列の型を明示的に保つため
};

// オブジェクトの型をそのままフォームの型として利用する
type FormState = typeof initialFormState;

// コンポーネントのプロパティや、stateの型定義として直結させる
const updateProfile = (newState: Partial) => {
// 差分更新のロジック
console.log(‘Updating state with:’, newState);
};

// 使うときはこんな感じ
updateProfile({ username: ‘Taro Engineer’ }); // OK

バックエンドのJSON構造が変わったら、モックデータのプロパティを書き換えるだけで、依存しているすべての型が自動的に追従する。これがリファクタリング耐性の高いコードの作り方だ。

パターンC: 関数コンテキストや戻り値の型キャプチャ

複雑なロジックを持つユーティリティ関数や、非同期のAPIクライアント関数の戻り値の型をそのまま利用したいときにも `typeof` は光る。

// 既存の複雑なデータフェッチ関数(外部ライブラリのものとする)
async function fetchUserData(userId: string) {
return {
id: userId,
profile: {
name: ‘TypeScript Man’,
age: 28,
},
permissions: [‘read’, ‘write’] as const,
};
}

// 関数の「戻り値の型」を Promise から剥ぎ取って取得する技法
// ReturnType と PromiseAwaited、そして typeof の合わせ技
type UserData = Awaited>;

// 抽出された型を使ったコンポーネント用のProps型
interface UserProfileCardProps {
user: UserData;
}

const UserProfileCard: React.FC = ({ user }) => {
return (

{user.profile.name}

年齢: {user.profile.age}

);
};

「関数の戻り値が変わるたびに型を書き直す」という前世紀の遺物のような作業から、君を解放してくれるイケてるテクニックだ。

—

3. シニアが教える「やりがちなアンチパターン」

ここまで `typeof` の素晴らしさを語ってきたが、実務でこれを振り回しすぎて事故るケースもある。最後に、やってはいけないアンチパターンをいくつか共有しておこう。

1. すべての型を `typeof` で済ませようとする

  • データベースのスキーマや、ドメインモデルの根幹に関わる部分まで「値からの逆算」で型を作ろうとすると、コードの依存関係がスパゲッティになる。ビジネスロジックの契約(Interface)は、ちゃんと明示的に書くべき場所もある。

2. `as const` を付け忘れて頭を抱える

  • 「あれ? `typeof` したのに `string` になっちゃうんだけど!」というバグの9割は `as const` の付け忘れ。定数定義の末尾には目を光らせよう。

3. スコープを意識していない

  • `typeof` はあくまで「その値がスコープ内に存在していること」が前提だ。モジュール外のプライベートな値や、型定義ファイル(`.d.ts`)のスコープ外にある値を参照しようとしてコンパイルエラーになるミスをよく見る。

—

まとめ

`typeof` 型演算子は、JavaScriptの「値」の世界と、TypeScriptの「型」の世界をエレガントに橋渡ししてくれる強力なツールだ。

「DRY原則(Don’t Repeat Yourself)」はコードのロジックだけでなく、型定義の世界にも適用されるべきだと俺は思っている。値と型の二重管理をやめ、`typeof` を味方につけることで、君の書くコードはより堅牢で、変更に強いものに生まれ変わるはずだ。

さあ、今日のプルリクエストから、不要な型定義をガンガン削ぎ落していこうぜ!何か疑問があれば、いつでも俺の席に聞きに来てくれ。

コメント

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