TypeScript混入能否为独立泛型类添加泛型函数?
TypeScript泛型混入的适配问题
我需要基于后期才会确定具体类型的泛型数据结构,实现TypeScript的混入功能,而且要应用混入的基类本身也带有泛型特性。
非泛型混入应用到泛型类上是完全可行的,比如下面的例子:
class Dog<TFavoriteToy extends object> extends WithHouse(Pet)<TFavoriteToy> {}
但当混入自身需要类型参数化时,我发现只有在完全指定所有类型参数的情况下才能正常应用。以下是具体的示例和遇到的问题:
示例代码
抽象基类
/** * 带有部分实现的抽象基类 */ export class BaseThing<TData extends object> { dataSource: TData; constructor(data: TData) { this.dataSource = data; } } type Constructor<T> = (new (...args: any[]) => T);
泛型混入函数
/** * 带有泛型功能的混入 */ export function WithCustomer< T extends Constructor<{}>, TCustomer extends {name: string} >(SuperClass: T) { return class extends SuperClass { _cnames: string[]; constructor(...args: any[]) { super(...args); this._cnames = []; } logCustomer(customer: TCustomer): TCustomer { this._cnames.push(customer.name); return customer; } }; }
可行的用法(完全指定类型参数)
当基类和混入的所有类型参数都明确时,代码可以正常运行:
// 定义具体的类型实例 type TActualData = {count: number}; type TActualCust = {name: string; phone: string}; export class WorkingThing extends WithCustomer< Constructor<BaseThing<TActualData>>, TActualCust >(BaseThing) { constructor(data: TActualData) { super(data); this.logCustomer({name: 'Joe', phone: '123'}); } }
有问题的用法(未解析泛型时应用)
尝试定义保留泛型特性的子类时,会触发TypeScript错误:No base constructor has the specified number of type arguments. ts(2508)
/** * 无法正常运行的类:试图在未解析底层泛型的情况下应用混入 */ export class BrokenThing<TData, TCustomer> extends WithCustomer(BaseThing)<TData, TCustomer> { // 计划实现泛型相关的额外功能 }
我尝试过调整extends表达式中类型注解的位置,但都无法解决问题。从原型链的逻辑来看这种用法应该是可行的,希望能找到对应的语法技巧解决这个问题。
解决方案:调整混入函数的泛型结构
核心思路是把混入的泛型参数拆分,让WithCustomer先接收自身需要的TCustomer类型参数,再返回一个接收基类构造函数的函数,最终返回的类可以兼容基类的泛型参数。
调整后的混入函数
type Constructor<T = {}> = new (...args: any[]) => T; /** * 调整后的泛型混入:先接收TCustomer参数,再接收基类 */ export function WithCustomer<TCustomer extends {name: string}>() { return function <TBase extends Constructor>(SuperClass: TBase) { return class extends SuperClass { _cnames: string[]; // 用ConstructorParameters约束构造函数参数,类型更安全 constructor(...args: ConstructorParameters<TBase>) { super(...args); this._cnames = []; } logCustomer(customer: TCustomer): TCustomer { this._cnames.push(customer.name); return customer; } }; }; }
正确的泛型子类定义
现在可以正常定义保留泛型特性的子类:
export class FixedThing<TData extends object, TCustomer extends {name: string}> extends WithCustomer<TCustomer>()(BaseThing)<TData> { // 添加泛型相关的额外功能 getCustomerNames(): string[] { return this._cnames; } } // 测试使用 const myThing = new FixedThing({count: 10}); myThing.logCustomer({name: "Alice", email: "alice@example.com"}); console.log(myThing.getCustomerNames()); // 输出 ["Alice"]
这种写法通过拆分泛型参数的传递顺序,让混入和基类的泛型都能在子类中保留,完美解决了未解析泛型时的混入应用问题。
内容的提问来源于stack exchange,提问作者dingus
相关产品推荐
相关产品推荐

