如何在TypeScript中实现请求响应关联的泛型元组?
问题需求
实现一个请求处理函数,要求:
- 接收特定类型的请求标识
- 每个请求标识对应固定类型的响应参数,该参数需传入
sendResponse回调函数
请求与响应的类型映射定义如下:
type OneChoice = ['123', string] | ['456', boolean];
即:
- 请求为
'123'时,sendResponse的参数必须是string类型 - 请求为
'456'时,sendResponse的参数必须是boolean类型
尝试的失败写法
我试了多种写法都没达到预期,比如下面的代码:
function handler1<T extends OneChoice>(request: T[0], sendResponse: (arg: T[1]) => void): null { if (request === '123') { sendResponse(false); // 这里本该编译失败,但实际通过了 } return null; }
后续尝试的方案(仍未解决)
按照建议尝试了以下两种写法,还是存在问题:
type OneChoiceMap = { [T in OneChoice as T[0]]: T[1] }; function handler2<K extends keyof OneChoiceMap>(request: K, sendResponse: (arg: OneChoiceMap[K]) => void): void { if (request === '123') { sendResponse('alpha'); // 报错,不符合预期! } } handler2('123', false); // 报错,符合预期 handler2('123', (arg: string) => {}); // 正常,符合预期 function handler3<K extends OneChoice[0]>(request: K, sendResponse: (arg: Extract<OneChoice, [K, any]>[1]) => void): void { if (request === '123') { sendResponse(false); // 正常通过,不符合预期!! } } handler3('123', false); // 报错,符合预期 handler3('123', (arg: string) => {}); // 正常,符合预期
正确实现方案
方案1:函数重载(推荐)
通过函数重载明确每个请求标识对应的回调参数类型,TypeScript能在调用和函数内部都正确做类型检查:
// 重载签名:定义每个请求对应的参数类型 function handler(request: '123', sendResponse: (arg: string) => void): void; function handler(request: '456', sendResponse: (arg: boolean) => void): void; // 实现签名:处理逻辑 function handler(request: OneChoice[0], sendResponse: (arg: string | boolean) => void): void { if (request === '123') { sendResponse('valid string'); // 正常通过 // sendResponse(false); // 编译报错,符合预期 } else if (request === '456') { sendResponse(true); // 正常通过 // sendResponse('invalid'); // 编译报错,符合预期 } } // 调用测试 handler('123', (arg) => { console.log(arg.toUpperCase()); }); // arg自动推断为string handler('456', (arg) => { console.log(arg ? 'yes' : 'no'); }); // arg自动推断为boolean // handler('123', (arg: boolean) => {}); // 编译报错,符合预期
方案2:映射类型+类型断言
如果需要基于映射类型实现,可以结合类型断言在函数内部明确回调的类型:
type OneChoiceMap = { '123': string; '456': boolean; }; function handler<K extends keyof OneChoiceMap>(request: K, sendResponse: (arg: OneChoiceMap[K]) => void): void { if (request === '123') { const typedSend = sendResponse as (arg: string) => void; typedSend('valid'); // 正常通过 // typedSend(false); // 编译报错,符合预期 } else if (request === '456') { const typedSend = sendResponse as (arg: boolean) => void; typedSend(true); // 正常通过 // typedSend('invalid'); // 编译报错,符合预期 } }
原因说明
之前的写法失败的核心原因是:TypeScript无法在函数内部自动窄化泛型参数的具体类型。泛型K或T被视为联合类型的父类,导致回调函数的参数类型被推断为联合类型,无法约束到当前请求对应的具体类型。而函数重载通过明确的签名定义,让TypeScript能精准匹配每个请求的类型规则。
内容的提问来源于stack exchange,提问作者Gaurang Tandon
相关产品推荐
相关产品推荐

