object 与 Object

2026-09-09 00:00    #typescript  

object、Object、{} 三个都表示"对象",但接受的范围差别很大,而且两个会把原始类型也算进去——这几乎从不是你想要的。

三者对比

 1// object、Object、{} 的区别(三者都容易误用)
 2
 3// ---- object(小写):任何非原始类型 ----
 4let obj1: object = { a: 1 };       // ✓
 5obj1 = [1, 2];                     // ✓ 数组也是 object
 6obj1 = () => {};                   // ✓ 函数也是 object
 7// @ts-expect-error TS2322: 原始类型不行
 8obj1 = 42;
 9// @ts-expect-error TS2322: 字符串是原始类型
10obj1 = "hello";
11// @ts-expect-error TS2322: 布尔也是原始类型
12obj1 = true;
13obj1 = null as unknown as object;  // 需要断言才过
14
15// 但 object 不描述结构,访问属性会报错
16// 注意:这行是运行时真会崩的(obj1 此时是 null),所以只做类型验证,不执行
17function accessOnObject(o: object) {
18  // @ts-expect-error TS2339: Property 'a' does not exist on type 'object'.
19  return o.a;
20}
21void accessOnObject;
22
23// ---- Object(大写):除 null/undefined 外的一切 ----
24let obj2: Object = { a: 1 };       // ✓
25obj2 = [1, 2];                     // ✓
26obj2 = "hello";                    // ✓ 字符串也算!
27obj2 = 42;                         // ✓ 数字也算!
28obj2 = true;                       // ✓
29// @ts-expect-error TS2322: null 不行
30obj2 = null;
31
32// 注意:Object 接受原始类型,这几乎从不是你想要的
33
34// ---- {}(空对象字面量类型):除 null/undefined 外的一切 ----
35let obj3: {} = { a: 1 };           // ✓
36obj3 = [1, 2];                     // ✓
37obj3 = "hello";                    // ✓ 字符串也算!
38obj3 = 42;                         // ✓
39obj3 = true;                       // ✓
40// @ts-expect-error TS2322: null 不行
41obj3 = null;
42
43// {} 与 Object 行为一致,但写法更容易误解为"空对象"
44
45// ---- 对比:真正表达"任意对象"的写法 ----
46// 用 Record<string, unknown> 或具体结构
47let obj4: Record<string, unknown> = { a: 1 };
48// @ts-expect-error TS2322: 数组不是 Record<string, unknown>
49obj4 = [1, 2];
50
51// 或者明确不关心结构时用 unknown
52let obj5: unknown = { a: 1 };
53obj5 = 42;                         // unknown 接受一切
54
55// ---- 实践:函数参数该用哪个 ----
56// 想要"任何对象但不要原始类型" → object
57function keysOf(o: object): string[] {
58  return Object.keys(o);
59}
60keysOf({ a: 1 });
61keysOf([1, 2]);
62// @ts-expect-error TS2345: 原始类型不能传给 object
63keysOf("hello");
64
65// 想接受任何值 → unknown(最安全)
66function stringify(v: unknown): string {
67  return JSON.stringify(v);
68}
69
70console.log(obj1, obj2, obj3, obj4, obj5, keysOf({ a: 1 }), stringify({ a: 1 }));
1null null null [ 1, 2 ] 42 [ 'a' ] {"a":1}
类型接受 {a:1}接受 [1,2]接受 "str"接受 42接受 null
object✓✓✗✗✗
Object✓✓✓✓✗
{}✓✓✓✓✗

object(小写)

表示任何非原始类型。原始类型包括 string、number、boolean、symbol、bigint、null、undefined。

1let obj: object = { a: 1 };
2obj = [1, 2];          // ✓ 数组是对象
3obj = () => {};        // ✓ 函数是对象
4obj = 42;              // ✗ 原始类型
5obj = "hello";         // ✗ 原始类型

但它不描述结构,所以访问属性会报错:

1function accessOnObject(o: object) {
2  return o.a;
3  // error TS2339: Property 'a' does not exist on type 'object'.
4}

Object(大写)和 {}

两者行为完全一致(实测可互相赋值):

1declare const a: {};
2declare const b: Object;
3const x: Object = a;   // ✓
4const y: {} = b;       // ✓

它们接受除 null/undefined 外的一切,包括原始类型:

1let obj: Object = "hello";   // ✓ 这几乎肯定不是你想要的
2obj = 42;                    // ✓
Object 和 {} 是"接受一切"的宽类型

因为 string、number 在 JavaScript 里都有对应的包装对象,TypeScript 认为它们满足 Object 和 {}。

所以 function f(x: Object) 和 function f(x: {}) 实际上等于"接受任何非 null 值"——和你写 unknown 差不多,但少了 unknown 的强制收窄保护。两者都应该避免使用。

实测:{} 的真实含义

{} 常被误解为"空对象",但它其实是"没有已知属性"——任何有属性的值都满足它,包括字符串和数字。

1let obj3: {} = { a: 1 };   // ✓
2obj3 = [1, 2];             // ✓
3obj3 = "hello";            // ✓
4obj3 = 42;                 // ✓
5obj3 = null;               // ✗

所以 {} 的问题是它不检查任何东西,却看起来像在检查。

该用哪个

需求用什么
任何非原始类型object
任何值(最安全)unknown
有结构的对象具体类型或 Record<K, V>
任何非 null 值unknown(不要用 Object/{})

实践建议:

1// ✓ 要"任何对象但排除原始类型" → object
2function keysOf(o: object): string[] {
3  return Object.keys(o);
4}
5
6keysOf({ a: 1 });      // ✓
7keysOf([1, 2]);        // ✓
8keysOf("hello");       // ✗ 原始类型被拒绝
1// ✓ 要"任何值" → unknown(强制调用方收窄)
2function stringify(v: unknown): string {
3  return JSON.stringify(v);
4}
1// ✗ 避免
2function bad1(x: Object) {}
3function bad2(x: {}) {}
为什么 unknown 比 Object 好

unknown 要求你在使用前先收窄(见 类型收窄),而 Object 允许你直接调用方法:

1function withObject(x: Object) {
2  x.toString();       // 编译通过,但 x 可能是 null 之外任何东西
3}
4
5function withUnknown(x: unknown) {
6  x.toString();       // error TS18046: 'x' is of type 'unknown'.
7}

unknown 强制你显式处理,Object 静默放行。

相关