typeof 的两种含义

2026-09-09 00:00    #typescript  

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类型静态侧(构造函数)

相关