TypeScript の typeof は「値から型を取り出す道具」
まず一番大事なポイントからいきます。
TypeScript の typeof は、「ある値(変数・オブジェクトなど)から、その“型”を取り出すための演算子」です。
JavaScript にも typeof がありますが、あれは実行時に "string" や "number" といった「文字列」を返します。
一方 TypeScript の typeof は、コンパイル時に「型」として扱われるものです。
const user = {
id: 1,
name: "Taro",
};
type User = typeof user;
TypeScriptここで User は、次のような型になります。
type User = {
id: number;
name: string;
};
TypeScriptつまり、「user という値の“形”を、そのまま型として取り出した」のが typeof user です。
まずは基本パターン:変数から型を作る
オブジェクトから型を取り出す
const config = {
env: "dev",
debug: true,
};
type Config = typeof config;
TypeScriptこのとき Config は、次のような型になります。
type Config = {
env: string;
debug: boolean;
};
TypeScriptここでのポイントは二つです。
一つ目は、「値を一度書けば、その構造を型として再利用できる」こと。
二つ目は、「値の形を変えれば、型も自動で追従する」ことです。
const config = {
env: "dev",
debug: true,
version: 1,
};
type Config = typeof config;
// version: number が自動で追加される
TypeScript型を手書きで同期させる必要がなくなるので、「定数オブジェクト → その型」という流れがとても楽になります。
配列から型を取り出す
const numbers = [1, 2, 3];
type Numbers = typeof numbers;
TypeScriptこのとき Numbers は number[] になります。
つまり、「numbers は number の配列だ」と型として認識されます。
配列の中身の型だけ欲しいときは、typeof とインデックスアクセスを組み合わせます。
type NumberElement = (typeof numbers)[number];
// number
TypeScript[number] は「配列のどのインデックスでもいいから、その要素の型」という意味です。
初心者のうちは「(typeof 配列)[number] で“要素の型”が取れる」とだけ覚えておけばOKです。
関数から型を取り出す(引数・戻り値)
関数そのものの型
function greet(name: string): string {
return `Hello, ${name}`;
}
type GreetFn = typeof greet;
TypeScriptGreetFn は、「(name: string) => string」という関数型になります。
つまり、「greet と同じシグネチャ(引数と戻り値)を持つ関数の型」です。
const fn: GreetFn = (n) => `Hi, ${n}`; // OK
TypeScript「この関数と同じ形の関数を別のところでも使いたい」ときに、
「関数の実装 → typeof で型を取り出す → その型を再利用する」という流れが使えます。
引数や戻り値の型を取り出す(少し応用)
ここは「こんなこともできるんだ」くらいで大丈夫ですが、雰囲気だけ。
function createUser(name: string, age: number) {
return { name, age };
}
type CreateUser = typeof createUser;
type User = ReturnType<CreateUser>;
TypeScriptReturnType<T> は、関数型 T の戻り値の型を取り出すユーティリティ型です。
ここでは User が { name: string; age: number } になります。
typeof と組み合わせることで、「実装から型を逆算して取り出す」ことができるようになります。
typeof と as const を組み合わせると「リテラルな型」が取れる
そのまま typeof を使うと「広い型」になる
const user = {
id: 1,
role: "admin",
};
type User = typeof user;
TypeScriptこのとき User["role"] の型は string になります。"admin" という具体的な値から作っているのに、「role は string」としか見なされません。
as const で「値をそのまま型にする」
const user = {
id: 1,
role: "admin",
} as const;
type User = typeof user;
TypeScriptこうすると、User は次のような型になります。
type User = {
readonly id: 1;
readonly role: "admin";
};
TypeScriptつまり、
idは「number」ではなく「リテラルな 1」roleは「string」ではなく「リテラルな “admin”」
として扱われます。
このテクニックは、「定数テーブルから型を作る」ときにめちゃくちゃよく使います。
const ROLES = ["admin", "user", "guest"] as const;
type Role = (typeof ROLES)[number];
// "admin" | "user" | "guest"
TypeScriptここでは、
typeof ROLESでreadonly ["admin", "user", "guest"]という型を取り出し[number]で「その配列の要素の型」を取り出している
という流れです。
「値の配列 → その値のユニオン型」という変換が、typeof を使うと一気に楽になります。
typeof をどういうときに使うか(実務イメージ)
「値を先に書いて、その形を型として使いたい」とき
たとえば、設定オブジェクトや定数テーブルを先に書いて、
それを元に型を作りたい場面はよくあります。
const config = {
apiBaseUrl: "https://example.com",
timeout: 5000,
} as const;
type Config = typeof config;
TypeScriptこうしておけば、
configを実際に使うコードでは「値」として使えるConfigを型として使うコードでは「構造の保証」として使える
という二重のメリットが得られます。
「実装を変えたら型も自動で変わってほしい」とき
関数やオブジェクトの実装を変えたときに、
型定義を手で直すのはミスの元です。
typeof を使えば、
- 実装を変える
- それを参照している型も自動で変わる
という状態を作れます。
const messages = {
hello: "Hello",
goodbye: "Goodbye",
};
type MessageKey = keyof typeof messages;
// "hello" | "goodbye"
TypeScriptここで messages に "welcome" を足せば、MessageKey にも "welcome" が自動で追加されます。
「値 → typeof → keyof」という流れは、かなり頻出のパターンです。
初心者がまず掴んでおきたい typeof の感覚
最後に、感覚的なまとめをしておきます。
TypeScript の typeof は、
「この“値”と同じ形をした“型”が欲しい」と思ったときに使うスイッチです。
その結果として、
- オブジェクトや配列の「形」を、そのまま型として再利用できる
- 関数のシグネチャ(引数・戻り値)を型として取り出せる
as constと組み合わせて、「値の集合 → リテラル型のユニオン」に変換できる
という強力な表現が手に入ります。
まずは小さく、
const user = { id: 1, name: "Taro" };
type User = typeof user;
TypeScriptこのレベルから慣れていって、
「値を書いたら、その形を型としても使えるんだ」という感覚を身体に染み込ませていくと、typeof は一気に“使える道具”に変わっていきます。
