TypeScript类型兼容问题:MyRequest<T>无法赋值给MyRequest<APIResponseType>
问题描述
我的TypeScript代码如下:
export type APIResponseType = string | number; class QueueRequest { private readonly requests: Record<string, MyRequest<APIResponseType>[]> = {}; public addRequest<T extends APIResponseType>(request: MyRequest<T>) { if (!Object.prototype.hasOwnProperty.call(this.requests, request.url)) { this.requests[request.url] = []; } this.requests[request.url].push(request); // 第14行报错 } } const queueRequest = new QueueRequest(); class MyRequest<TResponse> { readonly url: string; private onSuccess: (response: TResponse) => void; constructor(url: string, onSuccess: (response: TResponse) => void) { this.url = url; this.onSuccess = onSuccess; } } function updateState(request: MyRequest<string>) { makeRequest(request); } function updateProperties(request: MyRequest<number>) { makeRequest(request); } function makeRequest<T extends APIResponseType>(request: MyRequest<T>) { queueRequest.addRequest(request) }
第14行遇到的错误:
Argument of type 'MyRequest<T>' is not assignable to parameter of type 'MyRequest<APIResponseType>'. Type 'APIResponseType' is not assignable to type 'T'. 'APIResponseType' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'APIResponseType'. Type 'string' is not assignable to type 'T'. 'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'APIResponseType'.
将第4行的APIResponseType改为any可消除错误,但我希望在不使用any的前提下限制类型范围,同时让QueueRequest能够容纳不同类型的MyRequest,请问该如何解决?
解决方案
核心问题是TypeScript中函数参数的逆变特性:MyRequest<T>不能直接赋值给MyRequest<APIResponseType>,因为onSuccess方法的参数类型要求更严格。以下是两种安全且符合需求的解决方式:
方法一:安全类型断言适配存储类型
既然T本身就是APIResponseType的子类型,我们可以用类型断言绕过逆变限制,同时保留类型约束:
class QueueRequest { private readonly requests: Record<string, MyRequest<APIResponseType>[]> = {}; public addRequest<T extends APIResponseType>(request: MyRequest<T>) { if (!Object.prototype.hasOwnProperty.call(this.requests, request.url)) { this.requests[request.url] = []; } // 断言为MyRequest<APIResponseType>,因T属于APIResponseType子类型,无安全风险 this.requests[request.url].push(request as MyRequest<APIResponseType>); } }
方法二:用unknown替代any存储请求
用unknown代替any作为存储的基础类型,既保留类型约束,又允许容纳所有符合要求的MyRequest实例:
class QueueRequest { // 用unknown代替any,保证类型安全 private readonly requests: Record<string, MyRequest<unknown>[]> = {}; public addRequest<T extends APIResponseType>(request: MyRequest<T>) { if (!Object.prototype.hasOwnProperty.call(this.requests, request.url)) { this.requests[request.url] = []; } this.requests[request.url].push(request); } // 处理请求时可通过类型守卫或断言匹配响应类型 public processRequests(url: string) { const reqs = this.requests[url]; if (!reqs) return; // 模拟API返回,实际场景根据真实响应做类型判断 const response: APIResponseType = Math.random() > 0.5 ? 'success' : 200; reqs.forEach(req => { (req as MyRequest<typeof response>).onSuccess(response); }); } }
内容的提问来源于stack exchange,提问作者tskr
相关产品推荐
相关产品推荐

