TypeScript:如何在Button基类中忽略Connection泛型的具体类型?
TypeScript泛型抽象基类兼容解决方案
问题背景
我定义了泛型抽象基类Connection<Message, RawData>,并实现了SomeConnection等子类。现在需要创建Button基类来操作多种Connection子类,但声明Button<Connections extends Record<string, Connection<???>>>时,无法确定如何让TypeScript忽略Connection的具体泛型类型,求解决方案。
相关原始代码如下:
// Connection抽象类 abstract class Connection<Message, RawData> { messageHandlers: Array<(msg: Message) => void> = []; constructor(public path: string) {} public send(message: Message): void { const data = this.formatData(message); this.realSend(data); } abstract connect(): void; abstract formatData(message: Message): RawData; abstract realSend(data: RawData): void; } // SomeConnection子类 import {Buffer} from 'node:buffer'; interface SomeMessage { id: number; value: number; } class SomeConnection extends Connection<SomeMessage, Buffer> { override connect(): void { someLibrary.connect(this.path); someLibrary.on('message', (data: SomeMessage) => { for (const handler of this.messageHandlers) { handler(data); } }); } override formatData(message: SomeMessage): Buffer { const str = `${message.id};${message.value}` return Buffer.from(str); } override realSend(data: Buffer): Promise<void> { return someLibrary.write(this.path, data); } } // Button基类(问题处) abstract class Button<Connections extends Record<string, Connection< ??? >>> { // PROBLEM HERE! constructor(public connections: Connections) {} abstract onMouseClick(): void; } // MyButton子类 interface MyButtonConnections { some: SomeConnection; other: OtherConnection; // 假设该类存在 } class MyButton extends Button<MyButtonConnections> { constructor(public id: number, public clickCount: number = 0) { super({ some: new SomeConnection('http://localhost:1234'), other: new OtherConnection('http://example.com'), }); } override onMouseClick(): void { this.clickCount++; this.connections.some.send({id: this.id, value: this.clickCount}); this.connections.other.send(this.clickCount); // 假设Other的send接收number类型 } }
解决方案
方法1:使用unknown作为泛型通配符
这是最安全的方案,利用unknown作为所有类型的顶层类型,兼容任意Connection子类的泛型参数,同时保留类型检查:
abstract class Button<Connections extends Record<string, Connection<unknown, unknown>>> { constructor(public connections: Connections) {} abstract onMouseClick(): void; }
方法2:使用any(宽松模式)
如果不需要严格的类型校验,可以用any替代unknown,但会关闭TypeScript的类型检查能力,不推荐在严谨场景使用:
abstract class Button<Connections extends Record<string, Connection<any, any>>> { constructor(public connections: Connections) {} abstract onMouseClick(): void; }
方法3:定义非泛型基础接口(可选)
如果需要更清晰的类型分层,可以先定义非泛型的基础接口,让Connection继承该接口,再用接口约束Button的泛型:
// 定义非泛型基础接口 interface BaseConnection { path: string; connect(): void; send(message: unknown): void; } // 修改Connection抽象类继承BaseConnection abstract class Connection<Message, RawData> implements BaseConnection { // ... 原有代码保持不变 } // Button基类使用BaseConnection约束 abstract class Button<Connections extends Record<string, BaseConnection>> { constructor(public connections: Connections) {} abstract onMouseClick(): void; }
验证效果
修改后MyButton的使用完全正常,TypeScript会自动推导每个Connection子类的具体泛型类型,确保调用send时传入的参数类型正确,不会出现类型报错。
内容的提问来源于stack exchange,提问作者Mahi
相关产品推荐
相关产品推荐

