typeof 在 TypeScript 里是两个不同的运算符,同名但完全无关:值位置上是 JavaScript 的运行时运算符,类型位置上是 TypeScript 的类型查询运算符。
两种位置,两种含义
1// typeof 的两种含义:值位置 vs 类型位置
2
3const config = { debug: true, level: 1 };
4
5// ---- 值位置:JavaScript 的 typeof 运算符,返回字符串 ----
6const t1 = typeof config; // "object"(运行时字符串)
7const t2 = typeof 42; // "number"
8const t3 = typeof undefined; // "undefined"
9const t4 = typeof null; // "object" ← 历史遗留 bug
10
11// 用于类型收窄(值位置,但影响类型)
12function pad(x: string | number): string {
13 if (typeof x === "string") return x.padStart(2); // 收窄为 string
14 return x.toFixed(1);
15}
16
17// ---- 类型位置:TypeScript 的类型查询运算符,返回类型 ----
18type Config = typeof config; // { debug: boolean; level: number }
19type T1 = typeof t1; // string(因为 t1 是字符串)
20
21// 常用于从已有值推导类型
22const DEFAULT = { retries: 3, timeout: 1000 };
23type Options = typeof DEFAULT; // { retries: number; timeout: number }
24
25// 配合 keyof
26type OptionKey = keyof typeof DEFAULT; // "retries" | "timeout"
27
28// 配合 as const 保留字面量
29const LIMITS = { min: 0, max: 100 } as const;
30type Limits = typeof LIMITS; // { readonly min: 0; readonly max: 100 }
31
32// ---- 关键区别:typeof 后面是"值"还是"类型" ----
33
34// 类型位置:typeof 后面必须是值(变量、属性、函数)
35class Point { x = 0 }
36type PointCtor = typeof Point; // 构造函数类型 new () => Point
37type PointInstance = Point; // 实例类型(不用 typeof)
38
39// 类型位置:typeof 只能用于"值",不能用于纯类型
40// @ts-expect-error TS2693: 'string' only refers to a type, but is being used as a value here.
41type Bad2 = typeof string;
42
43type MyType = { a: number };
44// @ts-expect-error TS2693: 'MyType' only refers to a type, but is being used as a value here.
45type Bad3 = typeof MyType;
46
47// ---- 常见组合 ----
48// 1. 从常量推导联合类型
49const COLORS = ["red", "green"] as const;
50type Color = (typeof COLORS)[number]; // "red" | "green"
51
52// 2. 从函数推导类型
53function createUser(name: string, age: number) {
54 return { name, age, createdAt: new Date() };
55}
56type User = ReturnType<typeof createUser>;
57type UserParams = Parameters<typeof createUser>;
58
59// 3. 从模块推导类型
60const api = {
61 getUser: (id: string) => ({ id }),
62 listUsers: () => [],
63};
64type Api = typeof api;
65type ApiKeys = keyof Api; // "getUser" | "listUsers"
66
67// 4. 类的静态侧与实例侧
68class Factory {
69 static create() { return new Factory(); }
70 value = 1;
71}
72type FactoryStatic = typeof Factory; // 有 create
73type FactoryInstance = Factory; // 有 value
74
75const f1: FactoryStatic = Factory;
76const f2: FactoryInstance = new Factory();
77
78// ---- 陷阱:typeof 在类型位置不能用于表达式 ----
79// 下面这行是语法错误(typeof 后面只接受标识符/属性访问):
80// type Bad6 = typeof getValue();
81// error TS1005: ';' expected.
82
83function getValue() { return 1; }
84type OkType = typeof getValue; // ✓ 不加括号,取函数类型
85type Ret = ReturnType<typeof getValue>; // ✓ 用 ReturnType 拿返回类型
86
87console.log(t1, t2, t3, t4, pad("a"), pad(1));
88console.log(config, DEFAULT, LIMITS, COLORS, f1.create().value, f2.value);
89const check: Config = { debug: false, level: 2 };
90console.log(check, new Point().x);
91const u: User = createUser("a", 1);
92const p: UserParams = ["a", 1];
93console.log(u.name, p, api.getUser("1"), api.listUsers(), getValue(), 0 as Ret);
1object number undefined object a 1.0
2{ debug: true, level: 1 } { retries: 3, timeout: 1000 } { min: 0, max: 100 } [ 'red', 'green' ] 1 1
3{ debug: false, level: 2 } 0
4a [ 'a', 1 ] { id: '1' } [] 1 0
| 值位置 | 类型位置 | |
|---|---|---|
| 是什么 | JavaScript 运算符 | TypeScript 类型查询 |
| 返回 | 字符串("number") | 类型 |
| 何时求值 | 运行时 | 编译时 |
| 例子 | typeof x === "string" | type T = typeof x |
1const config = { debug: true, level: 1 };
2
3// 值位置:运行时字符串
4const t1 = typeof config; // "object"
5
6// 类型位置:编译时类型
7type Config = typeof config; // { debug: boolean; level: number }
值位置的 typeof
用于类型收窄(见 类型收窄):
1function pad(x: string | number): string {
2 if (typeof x === "string") return x.padStart(2); // 收窄为 string
3 return x.toFixed(1); // 收窄为 number
4}
能识别的八种结果:
1typeof "a" // "string"
2typeof 1 // "number"
3typeof true // "boolean"
4typeof 1n // "bigint"
5typeof Symbol() // "symbol"
6typeof undefined // "undefined"
7typeof (() => {}) // "function"
8typeof {} // "object"
typeof null === "object"这是 JavaScript 的历史遗留 bug。typeof x === "object" 不能排除 null,必须额外检查:
1if (typeof x === "object" && x !== null) { }
类型位置的 typeof
从已有的值推导类型,这是它最有价值的用途。
从常量推导类型
1const DEFAULT = { retries: 3, timeout: 1000 };
2type Options = typeof DEFAULT; // { retries: number; timeout: number }
好处是单一数据源:类型和值不会不一致。改 DEFAULT 的结构,Options 自动跟着变。
配合 keyof 取键
1type OptionKey = keyof typeof DEFAULT; // "retries" | "timeout"
顺序不能反。keyof 需要类型,typeof 把值变成类型。
配合 as const 保留字面量
1const LIMITS = { min: 0, max: 100 } as const;
2type Limits = typeof LIMITS; // { readonly min: 0; readonly max: 100 }
不加 as const 的话类型是 { min: number; max: number },丢了具体值。
从函数推导
1function createUser(name: string, age: number) {
2 return { name, age, createdAt: new Date() };
3}
4
5type User = ReturnType<typeof createUser>;
6type UserParams = Parameters<typeof createUser>;
这里 typeof createUser 得到函数类型,再用 ReturnType 取返回类型。
从数组推导联合
1const COLORS = ["red", "green"] as const;
2type Color = (typeof COLORS)[number]; // "red" | "green"
这是"从常量数组推导联合类型"的标准写法。
类的静态侧 vs 实例侧
这是 typeof 最容易被忽略的用途:
1class Factory {
2 static create() { return new Factory(); }
3 value = 1;
4}
5
6type FactoryStatic = typeof Factory; // 有 create(静态侧)
7type FactoryInstance = Factory; // 有 value(实例侧)
typeof Factory 得到的是构造函数类型,包含静态成员。不加 typeof 的 Factory 是实例类型。详见 类既是值又是类型。
类型位置的限制
typeof 在类型位置只能用于值,不能用于纯类型:
1type MyType = { a: number };
2
3type Bad3 = typeof MyType;
4// error TS2693: 'MyType' only refers to a type, but is being used as a value here.
5
6type Bad2 = typeof string;
7// error TS2693: 'string' only refers to a type, but is being used as a value here.
也不能用于表达式或函数调用——这是语法错误:
1type Bad6 = typeof getValue();
2// error TS1005: ';' expected.
要去函数的返回类型,用 ReturnType:
1function getValue() { return 1; }
2
3type OkType = typeof getValue; // ✓ 不加括号
4type Ret = ReturnType<typeof getValue>; // ✓ 1
速记
| 看到 | 位置 | 含义 |
|---|---|---|
typeof x === "string" | 值 | 运行时判断 |
type T = typeof x | 类型 | 编译时查询 |
keyof typeof x | 类型 | 先查询再取键 |
ReturnType<typeof f> | 类型 | 取函数返回类型 |
typeof Class | 类型 | 静态侧(构造函数) |