TypeScript | 基礎文法:オブジェクト基礎 – typeof の基本

TypeScript TypeScript
スポンサーリンク

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

このとき Numbersnumber[] になります。
つまり、「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;
TypeScript

GreetFn は、「(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>;
TypeScript

ReturnType<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 ROLESreadonly ["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 は一気に“使える道具”に変わっていきます。

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