• 作者:老汪软件技巧
  • 发表时间:2024-12-07 11:05
  • 浏览量:

00. Hello World

大家好,我是大家的林语冰。

在本文中,我们会结合 TS 源码,深度学习 Object 相关的技术细节:

Object = 接口 + 构造函数为什么 Object 不是 Object 类型Object vs ObjectConstructor01. TS 类型 vs JS 值

在 TS 中,一个 Object 代表了两种概念:

由于 Object 接口和构造函数同名,这有时容易混淆:

let o: Object = new Object()

这里的 Object 既用作值,也用作类型 —— 左侧类型注解中的 Object 代表 TS 接口,右侧 new 实例化中 Object() 则代表 JS 构造函数。

我们一般会根据经验法则来区分 —— Object 位于类型注解中则推断它是 TS 接口,Object() 后面紧跟 () 函数调用运算符则推断它是 JS 函数。

多数情况下,这种心智模型行得通,但有时可能会踩雷:

// prettier-ignore
let o: InstanceType<typeof Object> = new Object

上述代码中,TS 重载了 typeof 运算符,允许它在类型上下文使用;JS 允许无参构造时省略 ()(虽然合法,但不是最佳实践),这两个 Object 其实 都是 JS 值,不是 TS 接口。

换而言之,类型注解中的 Object 不一定都是 TS 类型。

当你对代码中的 Object 感到困惑时,一种想象练习是根据当前上下文尝试对 Object 逐步拆解:

let a1: Object = new Object()
let b1: InstanceType<typeof Object>
// 可以拆解为:
let ObjValue = Object
type ObjType = Object
let a2: ObjType = new ObjectValue() // ok
let b2: InstanceType<typeof ObjValue> // ok
let b3: InstanceType<typeof ObjType> // error

这样,我们可以直观地厘清 Object 到底是用作接口(TS 类型),还是用作函数(JS 值)。

当你熟练这种思维实验后,拆解过程可以内化为底层的心智模型,无需在 TS IDE 真实推理。

请记住,值和类型不是一回事,不能一概而论。Object 的不同位置和用法共同决定了它是作为值,还是作为类型。

02. Object 不是 Object 类型

有趣的是,Object 其实 不是 Object 类型,即使两者的拼写一模一样。

_object类中的常用方法_object类型的数据

由于 JS 可以使用 typeof 来查询运行时类型,所以 TS 也可以,即使是在类型上下文中:

type Constructor = typeof Object
// => ObjectConstructor
type Prototype = typeof Object.prototype
// => Object

可以看到,Object.prototype 是 Object 的原型属性,Object 接口描述的是它的类型;Object 是 Object.prototype 的构造函数,ObjectConstructor 描述的才是它的类型。

我们可以结合 TS 源码来理解:

// es5.d.ts
interface Object {
  constructor: Function
  toString(): string
  // ...
}
interface ObjectConstructor {
  readonly prototype: Object
  create(o: object | null): any
  // ...
}
declare var Object: ObjectConstructor

这里,Object 不能用来描述 Object() 构造函数的接口,因为在 TS 中,接口只能约束实例对象的类型,无法直接约束类的静态类型或构造函数类型。

03. 为何需要两个接口?

那么,JS 中有且仅有一个 Object 函数,为什么 TS 却需要声明两个接口来描述它呢?

这是因为,在运行时类类型是不存在的,TS 的静态类型只能存在于编译时。Object 不是由 TS 编译器提供,而是由 JS 运行时提供的。

当我们在 TS 中声明一个类时,实际上类声明会引入一个新的命名类型,即与类同名的类类型。类类型表示类的实例类型,它由类的实例成员类型构成。

同时,类类型还会引入构造函数类型,如果类上存在静态成语,该类类型还可以约束类对象本身。

本质上,在定义一个类时,实际上我们定义了一个构造函数。

随后,我们可以使用 new 运算符和该构造函数来创建类的实例。我们可以将该类型称作类的构造函数类型,在该类型中也包含了类的静态成员类型。


上一条查看详情 +GPT-VIS:让模型栩栩如生
下一条 查看详情 +没有了