You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript泛型<T>继承构造函数类型的原因及装饰器逻辑解析

问题解答

1. 泛型<T>为什么要继承{ new (...args: any[]): {} }?

这个类型是构造函数签名,用来约束泛型T必须是一个可实例化的类。因为我们的装饰器是用来装饰类的,传入的classConstructor必须是能通过new关键字创建实例的构造函数。

具体拆解:

  • new (...args: any[]):表示这个构造函数可以接收任意数量、任意类型的参数
  • : {}:表示构造函数执行后返回一个对象(也就是类的实例)

用这个约束能让TypeScript编译器自动校验传入的对象是否是合法的类构造函数,避免把非构造函数类型误传给装饰器导致错误。

2. 被继承的对象如何接收参数?

被继承的原类(这里是Person)通过子类构造器里的super(...args)接收参数。

比如当我们执行const p = new Person("Alice", 30)时,传入的"Alice"和30会被子类构造器的...args捕获,然后通过super(...args)传递给父类Person的构造器,父类就能正常初始化自身的name和age属性。

3. 装饰器函数的完整执行逻辑

我们一步步拆解整个流程:

步骤1:执行装饰器工厂函数

@AddLocation(1.234, 1.876)首先会触发AddLocation(1.234, 1.876)的执行,这个函数接收lat和long参数后,返回一个真正的装饰器函数(即接收classConstructor的箭头函数)。这一步会把lat和long保存在闭包里,后续可以直接使用。

步骤2:装饰器函数接收原类

返回的装饰器函数会被传入被装饰的Person类作为参数classConstructor。此时泛型约束会确保Person是符合构造函数签名的合法类型。

步骤3:创建并返回子类

装饰器函数返回一个继承自Person的子类:

  • 子类新增了mapLocation属性,类型为MapLocation
  • 子类构造器接收任意参数...args,首先调用super(...args)把参数传给父类Person的构造器,完成父类属性的初始化
  • 最后给当前实例的mapLocation赋值为闭包中保存的lat和long

步骤4:替换原类

TypeScript会用这个子类替换原来的Person类。之后我们创建Person实例时,实际创建的是这个子类的实例——它既有原Person的name、age属性,又有新增的mapLocation属性。

举个实例化的验证例子:

const person = new Person("Bob", 25);
console.log(person.name); // "Bob"
console.log(person.age); // 25
console.log(person.mapLocation); // { lat: 1.234, long: 1.876 }

内容的提问来源于stack exchange,提问作者Karan Badhwar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 02:50:34