やあ、調子はどうだい?現場でコードを書いていると、どうしても「型定義」と「実際の値」の二重管理に疲弊する瞬間があるよね。
「設定ファイルに新しい項目を追加したのに、型定義を更新し忘れてコンパイルエラーが出た」
「巨大なオブジェクトの構造をわざわざインターフェースに書き写すのが苦痛で仕方ない」
そんな君に、今日はTypeScriptにおける「究極の省エネ術」であり、かつ「真のDRY(Don’t Repeat Yourself)原則」を体現する武器、`typeof`演算子について話をしよう。
これは単なる便利機能じゃない。JavaScriptという「値」の世界から、TypeScriptという「型」の世界へ情報を橋渡しする、極めて重要な翻訳機なんだ。
—
1. JavaScriptの`typeof`と何が違うのか?
まず、ここを混同している中級者が意外と多い。
ブラウザ(ランタイム)が実行するJavaScriptの`typeof`は、値の型を「文字列」として返す。
// これはJavaScriptのランタイム挙動
const message = “Hello TS”;
console.log(typeof message); // “string” という「値」が返る
一方で、TypeScriptの型定義コンテキストで使う`typeof`は、「その変数が持っている型そのもの」を抽出する。
const message = “Hello TS”;
// これはTypeScriptの型抽出
type MessageType = typeof message; // MessageType は ‘string’ 型になる
実行時にはこの`typeof`の行は跡形もなく消え去る。ブラウザはこの型抽出を一切知らない。あくまで「開発者のエディタ上」と「コンパイル時」にだけ存在する、静的な解析なんだ。
—
2. 実務で輝く「単一ソース(Single Source of Truth)」の構築
現場でよくある「定数定義」を例に考えてみよう。例えば、アプリケーションのテーマカラーや設定値を管理するオブジェクトだ。
これを定義するとき、わざわざ `interface Theme { … }` を書いてから `const theme: Theme = { … }` と書くのは、正直言って二度手間だ。値が変わるたびに型を直すなんて、自動化の精神に反する。
実践コード:オブジェクトからの型抽出
// 1. まず「値」を定義する。これが唯一の真実(Single Source of Truth)になる。
// as const をつけることで、プロパティが読み取り専用かつリテラル型として固定される。
const APP_CONFIG = {
apiEndpoint: “https://api.example.com”,
retryCount: 3,
debugMode: false,
theme: {
primary: “#007bff”,
secondary: “#6c757d”
}
} as const;
// 2. typeof を使って、値から型を逆引きする
// これで、APP_CONFIGの構造が変われば、AppConfig型も自動的に追従する。
export type AppConfig = typeof APP_CONFIG;
// 3. 利用シーン
function initializeApp(config: AppConfig) {
console.log(`Connecting to: ${config.apiEndpoint}`);
}
// ちゃんと型補完が効き、誤ったプロパティへのアクセスはコンパイルエラーになる
initializeApp(APP_CONFIG);
この「値から型を作る」アプローチの何が素晴らしいかって、「ドキュメントとしての正確性」が100%担保されることだ。コードと型が乖離する隙を一切与えない。これがプロの仕事だよ。
—
3. `keyof` とのコンビネーションで真価を発揮する
`typeof` の真の力は、`keyof` 演算子と組み合わせた時に爆発する。
特定のオブジェクトのキーだけを許可する「Union型」を作りたいとき、手書きで `”primary” | “secondary”` と打つのはもうやめよう。
const STATUS_MESSAGES = {
SUCCESS: “処理が成功しました”,
ERROR: “エラーが発生しました”,
PENDING: “待機中です”,
} as const;
// 1. オブジェクトの「キー」を型として抽出する
// typeof STATUS_MESSAGES は { readonly SUCCESS: … }
// keyof はそのプロパティ名を取り出す
type StatusKey = keyof typeof STATUS_MESSAGES;
// StatusKey は “SUCCESS” | “ERROR” | “PENDING” 型になる
function updateStatus(status: StatusKey) {
const message = STATUS_MESSAGES[status];
console.log(message);
}
// updateStatus(“UNKNOWN”); // これはコンパイルエラー!安全だ。
updateStatus(“SUCCESS”); // OK
—
4. 関数やクラスのシグネチャを盗み取る
既存のライブラリや、他人が書いた関数の型定義が公開されていない(あるいは複雑すぎて追えない)場合、`typeof` は非常に強力なハックになる。
function complexLayoutEngine(width: number, height: number, options: { debug: boolean }) {
// 複雑な処理…
return { x: width / 2, y: height / 2 };
}
// 関数の「型」そのものを抽出
type EngineFunction = typeof complexLayoutEngine;
// 引数の型だけを抽出したいなら、組み込みの ReturnType や Parameters と組み合わせる
type EngineParams = Parameters
type EngineResult = ReturnType
// これで、元の関数がアップデートされても、依存している型は自動で更新される
—
5. 現場のアーキテクトが教える「使いどころの肝」
ここまで褒めちぎってきたが、何でもかんでも `typeof` で型を抽出すればいいというわけじゃない。
アンチパターン:複雑すぎる「型推論の迷宮」
何十階層もある巨大なオブジェクトから `typeof` で型を作ると、エラーが発生した時のエラーメッセージが読み解けなくなることがある。「どこで定義された型なのか」が追いづらくなるんだ。
使い分けの基準:
- 定数、設定値、固定のマスターデータ:`typeof` を積極的に使え。値が主役だからだ。
- ドメインロジック、ビジネスエンティティ(User, Orderなど):これらは `interface` や `type` で先に型を定義すべきだ。型が仕様(設計図)であり、値はその実装に過ぎないからだ。
アーキテクトの独り言
TypeScriptを使いこなすということは、「どこに静的な定義を置き、どこを動的な推論に任せるか」の境界線を引くことに他ならない。
`typeof` は、JavaScriptの柔軟な「値」の世界を、TypeScriptの堅牢な「型」の世界へ一瞬でテレポートさせる魔法だ。これを使いこなせれば、君のコードから冗長な記述が消え、変更に強く、それでいて型安全な、非常に洗練されたアーキテクチャが見えてくるはずだ。
さあ、今すぐ自分のプロジェクトにある「型と値の二重管理」を探してみな。そこが `typeof` の出番だ。応援しているよ。

コメント