内部函数调用带条件可选参数时的TypeScript报错排查
TypeScript泛型函数参数类型匹配问题解决
问题场景
我有一个useIsItemOnPlan泛型函数,接收Item类型参数,内部函数的参数类型依赖父函数传入的item是CountableItem还是NotCountableItem,这部分逻辑没问题,但调用getIsItemOnPlan和func3时,因为参数同时涉及父函数参数和可选参数,触发了TypeScript类型报错。
原始代码
type Item = 'water' | 'cake' | 'box' | 'banana'; type CountableItem = Exclude<Item, 'water'>; type NotCountableItem = Extract<Item, 'water'>; type Params = { [K in Item]: K extends CountableItem ? { quantity: number } : undefined }; function useIsItemOnPlan<K extends Item>(item: K) { const myFunc = (arg: Params[K]) => { // 这里调用getIsItemOnPlan时报错 return getIsItemOnPlan(item, arg); }; type MyFunc2Param = { onYes?: () => unknown; requirements: Params[K] }; const myFunc2 = (params: MyFunc2Param) => { // 这里调用func3有语法错误+类型报错 func3(requirements? {item, requirements} : {item}); }; } function getIsItemOnPlan<K extends Item>( t: K, ...args: [Params[K]] | (Params[K] extends undefined ? [] : never) ) { const [featureName, requirements] = [t, ...args]; return true; // 实际逻辑返回布尔值 } getIsItemOnPlan("water"); type MyRequest = | { item: CountableItem, requirements: {quantity: number} } | {item: NotCountableItem}; function func3(request: MyRequest) { console.log(request); }
报错原因分析
getIsItemOnPlan调用报错:
该函数的rest参数定义为[Params[K]] | (Params[K] extends undefined ? [] : never),当K是NotCountableItem时,Params[K]为undefined,此时函数只接受第一个参数,但代码中始终传入arg(即使是undefined),TypeScript无法自动匹配到无第二个参数的重载分支。func3调用报错:- 语法错误:未正确引用
params.requirements - 类型错误:构造的对象无法被TypeScript自动推断为
MyRequest联合类型,因为泛型K的类型信息在条件判断时未被正确收窄。
- 语法错误:未正确引用
解决方案
修复getIsItemOnPlan调用
通过类型守卫判断item类型,分别调用getIsItemOnPlan的不同参数分支,保证类型匹配:
const myFunc = (arg: Params[K]) => { if (item === 'water') { return getIsItemOnPlan(item); } else { return getIsItemOnPlan(item, arg as { quantity: number }); } };
修复func3调用
先修复语法错误,再通过条件判断构造符合MyRequest类型的对象:
const myFunc2 = (params: MyFunc2Param) => { const request: MyRequest = item !== 'water' ? { item, requirements: params.requirements as { quantity: number } } : { item }; func3(request); };
完整修复后代码
type Item = 'water' | 'cake' | 'box' | 'banana'; type CountableItem = Exclude<Item, 'water'>; type NotCountableItem = Extract<Item, 'water'>; type Params = { [K in Item]: K extends CountableItem ? { quantity: number } : undefined }; function useIsItemOnPlan<K extends Item>(item: K) { const myFunc = (arg: Params[K]) => { if (item === 'water') { return getIsItemOnPlan(item); } else { return getIsItemOnPlan(item, arg as { quantity: number }); } }; type MyFunc2Param = { onYes?: () => unknown; requirements: Params[K] }; const myFunc2 = (params: MyFunc2Param) => { const request: MyRequest = item !== 'water' ? { item, requirements: params.requirements as { quantity: number } } : { item }; func3(request); }; } function getIsItemOnPlan<K extends Item>( t: K, ...args: [Params[K]] | (Params[K] extends undefined ? [] : never) ) { const [featureName, requirements] = [t, ...args]; return true; } getIsItemOnPlan("water"); type MyRequest = | { item: CountableItem, requirements: {quantity: number} } | {item: NotCountableItem}; function func3(request: MyRequest) { console.log(request); }
内容的提问来源于stack exchange,提问作者GO VEGAN
相关产品推荐
相关产品推荐

