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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 04:55:18