关于 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
}
两个空间
类型空间(运行时会被擦除)
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 关键字 |
&、| | 交叉类型、联合类型 | 按位与、或 |
const | as const 上下文 | 变量声明 |
extends | 子类型/约束 | 子类 |
in | 映射类型 | for...in 循环 |
! | 非空断言 | 逻辑非 |
属性访问:[] 与 .
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)
如果某个符号在 JS 输出中消失了,它就只存在于类型空间。
抗压应对方案
1. “接口应该能用于 instanceof”
压力:“为什么 x instanceof MyInterface 不起作用?”
回应:instanceof 是运行时运算符。接口在运行时不存在。
**行动:**改用类型守卫或可辨识联合。
2. “typeof 应该能给出完整类型”
压力:“为什么 typeof x 只是 'object'?”
**回应:**你使用的是值空间中的 typeof。要获取 TypeScript 类型,请在类型注解中使用它。
**行动:**使用 type T = typeof x 获取完整的 TypeScript 类型。
危险信号——停下来重新考虑
- 对接口或类型别名使用
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 条:了解如何判断符号属于类型空间还是值空间。
评论
支持 Markdown