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

TypeScript中如何定义含不同泛型类的Record/对象?

解决方案

要实现一个键为字符串、值为带不同泛型的Endpoint实例的对象,同时保证类型安全(禁止存入非Endpoint值,且保留每个实例的具体泛型约束),可以通过以下两种方式实现:

1. 使用satisfies关键字(TypeScript 4.9+ 推荐)

satisfies可以让对象满足指定的类型约束,同时保留每个属性的具体类型推导结果,不会抹平泛型信息。

首先定义Endpoint类:

class Endpoint<Req, Res> {
  constructor(public request: Req, public response: Res) {}
}

然后定义对象时用satisfies约束所有值为Endpoint<any, any>:

const endpoints = {
  getUser: new Endpoint({ userId: 1 }, { name: "Alice", age: 30 }),
  createPost: new Endpoint({ title: "Hello", content: "World" }, { postId: "123" }),
} satisfies Record<string, Endpoint<any, any>>;

效果:

  • 禁止存入非Endpoint类型的值:如果尝试添加invalid: "oops"这类属性,TypeScript会直接报错。
  • 保留严格的泛型约束:访问endpoints.getUser.request.userId时,类型会被正确推导为number,endpoints.createPost.response.postId为string,无需额外类型断言。

2. 泛型映射类型(兼容旧版TypeScript)

如果你的TypeScript版本低于4.9,可以通过自定义泛型类型来实现相同效果:

class Endpoint<Req, Res> {
  constructor(public request: Req, public response: Res) {}
}

// 定义泛型类型,约束所有属性必须是Endpoint实例
type EndpointMap<T extends Record<string, Endpoint<any, any>>> = T;

// 使用时传入具体的属性类型,TypeScript会自动推导泛型
const endpoints: EndpointMap<{
  getUser: Endpoint<{ userId: number }, { name: string; age: number }>;
  createPost: Endpoint<{ title: string; content: string }, { postId: string }>;
}> = {
  getUser: new Endpoint({ userId: 1 }, { name: "Alice", age: 30 }),
  createPost: new Endpoint({ title: "Hello", content: "World" }, { postId: "123" }),
};

效果:

  • 同样会禁止非Endpoint值存入,且每个属性的泛型信息会被完整保留。
  • 缺点是需要显式声明每个属性的类型,比satisfies稍繁琐。

为什么不直接用Record<string, Endpoint<unknown, unknown>>?

直接使用这种定义会将所有Endpoint的泛型统一抹平为unknown,导致访问request或response时需要手动类型断言,失去了TypeScript的类型安全优势,无法满足你对请求/响应严格约束的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 18:45:48