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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:25:25