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
相关产品推荐
相关产品推荐

