type-value-space について
型空間と値空間のシンボルを区別したい TypeScript 開発者向けのスキルです。文脈による意味の違いや、`typeof` の二つの役割を説明します。
主な機能
- 01型空間と値空間の区別
- 02文脈に応じたシンボルの解釈
- 03型レベルと実行時の `typeof` の比較
- 04クラスとインターフェースの理解
活用シーン
- 01値として使えないシンボルのエラー調査
- 02`typeof` による型の取得
- 03分割代入引数の型注釈ミスの回避
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill type-value-spaceインストール後、Claude Code で/type-value-spaceを入力して読み込まれたか確認します。
詳細
シンボルが型空間にあるか値空間にあるかを見分ける方法を理解する
概要
TypeScript のすべてのシンボルは、型空間、値空間、またはその両方に存在します。
同じ名前でも、文脈によってまったく異なるものを指す場合があります。この違いを理解することは、TypeScript のコードを正しく読み解くために不可欠です。
このスキルを使う場面
- わかりにくい型エラーのデバッグ
- クラスとインターフェイスの動作の理解
typeofの正しい使い方- 型注釈を使った分割代入
- 列挙型の動作の理解
絶対的なルール
ALWAYS determine whether you're in type space or value space before interpreting a symbol.
覚えておきましょう:
- 型は実行時に消去されます(TypeScript Playground で確認してください)
- クラスは型と値の両方を導入します
typeofは空間によって意味が異なります- 分割代入の構文は型注釈の構文と異なります
見分け方:同じ名前、異なる意味
// Same name, different entities!
interface Cylinder {
radius: number;
height: number;
}
const Cylinder = (radius: number, height: number) => ({ radius, height });
// This refers to the VALUE (function), not the TYPE (interface)
if (shape instanceof Cylinder) {
// instanceof is a runtime operator - uses value space
}
2 つの空間
型空間(実行時に消去される)
type StringAlias = string; // Type alias
interface Person { name: string; } // Interface
type T = typeof myVar; // Type-level typeof
値空間(実行時に存在する)
const x = 123; // Variable
function add(a, b) { return a + b; } // Function
const t = typeof myVar; // Runtime typeof (returns string)
両方の空間(class と enum)
class MyClass {
value: number = 0;
}
// As a type: describes the shape of instances
const instance: MyClass = new MyClass();
// As a value: the constructor function
const ctor = MyClass; // typeof ctor is typeof MyClass
空間は文脈によって決まる
: または as の後 = 型空間
const alice: Person = { name: 'Alice' };
// ^^^^^^ Type space
const bob = someValue as Person;
// ^^^^^^ Type space
= の後 = 値空間
const x = Person; // Value space (error if Person is only a type)
関数シグネチャでは交互に現れる
function email(to: Person, subject: string, body: string): Response {
// ^^ ^^^^^^^ ^^^^ ^^^^^^^^ Types
// ^^^^^^^ ^^ ^^^^^^^ ^^^^^^ ^^^^ ^^^^ Values
}
typeof:空間によって異なる
型空間の typeof
const person = { name: 'Alice', age: 30 };
type PersonType = typeof person;
// ^? type PersonType = { name: string; age: number }
// Gets the TypeScript type of a value
値空間の typeof
const person = { name: 'Alice', age: 30 };
const t = typeof person; // "object"
// JavaScript's runtime typeof - only 8 possible values:
// "string", "number", "boolean", "undefined",
// "object", "function", "symbol", "bigint"
その他の二重の意味を持つ構文
| 構文 | 型空間 | 値空間 |
|---|---|---|
typeof | 値の TypeScript の型 | JS の実行時の型(8 種類) |
this | this の型 | JS の this キーワード |
&、| | 交差型、共用体型 | ビット単位の AND、OR |
const | as const のコンテキスト | 変数宣言 |
extends | サブタイプ/制約 | サブクラス |
in | マップ型 | for...in ループ |
! | 非 null アサーション | 論理 NOT |
プロパティアクセス:[] と .
interface Person {
first: string;
last: string;
}
// Value space: both work
const name1 = person['first'];
const name2 = person.first;
// Type space: ONLY brackets work
type First = Person['first']; // OK: string
type First2 = Person.first; // Error!
よくある間違い:型を使った分割代入
誤り:値の位置に型名を置く
function email({
to: Person, // Error! 'Person' implicitly has 'any' type
subject: string, // Error! 'string' implicitly has 'any' type
}) { }
// This creates variables named Person and string!
正しい方法:型と分割代入を分ける
function email(
{ to, subject, body }: { to: Person; subject: string; body: string }
) { }
// Destructures values, then annotates the whole parameter
TypeScript Playground の使用
Playground では、何が消去されるかを確認できます。
interface Person { name: string; } // Disappears in JS
type StringType = string; // Disappears in JS
class MyClass { } // Stays (has runtime value)
const x = 42; // Stays (has runtime value)
JavaScript の出力からシンボルが消えている場合、それは型空間にのみ存在していたということです。
プレッシャー耐性プロトコル
1. 「インターフェイスでも instanceof が使えるはず」
プレッシャー: 「なぜ x instanceof MyInterface は機能しないのですか?」
回答: instanceof は実行時の演算子です。インターフェイスは実行時には存在しません。
対応: 代わりに型ガードまたは判別可能な共用体を使います。
2. 「typeof で完全な型がわかるはず」
プレッシャー: 「なぜ typeof x は単に 'object' なのですか?」
回答: 値空間の typeof を使っています。TypeScript の型を得るには、型注釈で使います。
対応: 完全な TypeScript の型を得るには、type T = typeof x とします。
危険信号 — 立ち止まって考え直す
- インターフェイスまたは型エイリアスに対する
instanceof - 実行時の
typeofでオブジェクトの形状を区別できると期待している - 予期しない変数名が作られる分割代入
- 「値として使用できません」という型エラー
よくある自己正当化(いずれも誤り)
| 言い訳 | 実際のところ |
|---|---|
| 「クラスとインターフェイスは同じだ」 | クラスは実行時の値を持ちますが、インターフェイスは持ちません |
| 「typeof はどこでも同じように機能する」 | 型空間と値空間では意味がまったく異なります |
| 「型名を使って分割代入できる」 | 分割代入は値空間を使い、型注釈は型空間を使います |
クイックリファレンス
// Type space indicators:
type X = ... // After 'type'
interface X { } // After 'interface'
x: Type // After ':'
as Type // After 'as'
// Value space indicators:
const x = ... // After '='
x instanceof Y // instanceof operand
typeof x // Without type annotation
// Both spaces:
class X { } // Introduces type and value
enum X { } // Introduces type and value
要点
文脈によって、シンボルが型を指すか値を指すかが決まります。
同じ名前でも、まったく異なる意味を持つことがあります。コードがわかりにくいときは、まずどちらの空間にいるかを判断してください。TypeScript Playground で何が消去されるか確認しましょう。それが型空間です。
参考文献
Dan Vanderkam 著『Effective TypeScript』の項目 8「シンボルが型空間にあるか値空間にあるかを見分ける方法を理解する」に基づいています。