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

如何从泛型可区分联合中根据请求类型提取响应类型?

根据请求类型匹配对应响应类型的问题与解决方案

需要建立请求与响应类型的关联,仅根据请求类型从请求/响应对中提取对应的响应类型。当前handleRequest函数中使用Extract类型无法正确收窄泛型类型,导致返回值出现类型不匹配报错。

原类型定义

interface AbstractWorkerRequest {
  id: string
  type: string
}
interface AbstractWorkerResponse {
  id: string
  type: string
}
type WorkerExchange<REQUEST extends AbstractWorkerRequest, RESPONSE extends AbstractWorkerResponse> = {
    type: REQUEST['type']
    response: RESPONSE
    request: REQUEST
}

// 定义请求/响应对
type InitRequest = AbstractWorkerRequest & {
  type: 'init'
  body: number
}
type InitResponse = AbstractWorkerResponse & {
  type: 'init'
  body: { id: number }
}
type InitRequest2 = AbstractWorkerRequest & {
  type: 'init2'
  body: number
}
type InitResponse2 = AbstractWorkerResponse & {
  type: 'init2'
  body: { test: number }
}
type Exchanges = WorkerExchange<InitRequest, InitResponse> | WorkerExchange<InitRequest2, InitResponse2>
type AnyRequest = Exchanges['request']
type AnyResponse = Exchanges['response']

原尝试代码(存在报错)

type TypeOfResponse<T extends AnyRequest> = Extract<AnyResponse, { type: T['type'] }> // 此处无法正确收窄类型

function clientCode() {
    const request: InitRequest = {
        id: '',
        type: 'init',
        body: -1
    }
    const result = createPromise(request)// result应属于InitResponse类型
    console.log(result.body.id)
}
function createPromise<T extends AnyRequest>(request: T): TypeOfResponse<T> {
    const result = handleRequest(request)
    return result
}

function handleRequest<T extends AnyRequest>(request: T): TypeOfResponse<T> {
    switch(request.type) {
        case "init": {
            const results: TypeOfResponse<InitRequest> = {
                id: '',
                type: 'init',
                body: { id: -1 },
            }
            return results // 报错:Type 'InitResponse' is not assignable to type 'Extract<InitResponse2, { type: T["type"]; }>'
        }
        case 'init2': {
            const results: InitResponse2 = {
                id: '',
                type: 'init2',
                body: { test: -1 }
            }
            return results // 报错:Type 'InitResponse2' is not assignable to type 'Extract<InitResponse2, { type: T["type"]; }>'.
        }
    }
}

解决方案(不修改createPromise和clientCode)

1. 修正TypeOfResponse类型定义

通过Exchanges联合类型直接关联请求与响应,替代原有的Extract写法,让TypeScript能准确推断对应关系:

// 方式一:通过Extract关联请求对应的Exchange,再取response类型
type TypeOfResponse<T extends AnyRequest> = Extract<Exchanges, { request: T }>['response']

// 方式二:先创建请求类型到响应类型的映射表,再通过类型索引
// type ResponseMap = { [E in Exchanges as E['type']]: E['response'] }
// type TypeOfResponse<T extends AnyRequest> = ResponseMap[T['type']]

2. 调整handleRequest函数的类型推断

为了让switch分支的返回值能被正确识别,使用类型断言辅助推断:

function handleRequest<T extends AnyRequest>(request: T): TypeOfResponse<T> {
    switch(request.type) {
        case "init": {
            const results: InitResponse = {
                id: '',
                type: 'init',
                body: { id: -1 },
            }
            return results as TypeOfResponse<T>
        }
        case 'init2': {
            const results: InitResponse2 = {
                id: '',
                type: 'init2',
                body: { test: -1 }
            }
            return results as TypeOfResponse<T>
        }
        default:
            throw new Error(`Unknown request type: ${request.type}`)
    }
}

解释

原Extract<AnyResponse, { type: T['type'] }>的写法在泛型T未被具体实例化时,TypeScript无法确定T['type']对应的具体响应类型,导致返回值类型无法收窄。而通过Exchanges联合类型直接关联请求和响应的方式,能让TypeScript精准定位到当前请求对应的响应类型,解决类型不匹配的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 11:22:08