如何根据参数值在TypeScript函数内部缩小返回类型?
问题描述
我尝试将一个接口的多个方法合并为一个带额外operation参数的函数,最终函数能正常调用,但函数内部无法正确缩小返回类型。之前的方案解决了参数类型缩小的问题,但返回类型的困扰依然存在:
interface Machine { doSomething: (arg: number) => string; doSomethingElse: (args: boolean) => number; }; type MachineParameters = { [K in keyof Machine]: [K, ...Parameters<Machine[K]>]; }[keyof Machine]; const execute = <T extends MachineParameters>(...args: T): ReturnType<Machine[T[0]]> => { switch (args[0]) { case "doSomething": // 错误:Type '`${number}`' is not assignable to type 'MachineReturnTypes[T[0]]'. return `${args[1]}`; case "doSomethingElse": // 错误:Type 'number' is not assignable to type 'MachineReturnTypes[T[0]]'. return Number(args[1]); default: throw new Error("Unknown operation"); } }; // 正常:string类型 const x = execute("doSomething", 1); // 正常:number类型 const y = execute("doSomethingElse", true);
解决方案
问题根源在于泛型T是MachineParameters的子集,TypeScript无法在switch分支中将T[0]与具体操作字符串关联,也就无法推断返回类型和T的对应关系。以下三种方法可以解决这个问题:
方法一:使用函数重载
通过显式定义重载签名,让TypeScript明确每个操作对应的参数和返回类型:
interface Machine { doSomething: (arg: number) => string; doSomethingElse: (args: boolean) => number; }; // 定义重载签名,明确每个操作的参数和返回值 function execute(operation: "doSomething", arg: number): string; function execute(operation: "doSomethingElse", arg: boolean): number; // 实现签名,内部可以灵活处理 function execute(operation: keyof Machine, ...args: any[]): any { switch (operation) { case "doSomething": return `${args[0]}`; case "doSomethingElse": return Number(args[0]); default: throw new Error("Unknown operation"); } }; // 调用时类型推断正常 const x = execute("doSomething", 1); // string const y = execute("doSomethingElse", true); // number
方法二:类型断言收窄返回类型
如果想保留泛型写法,可以在每个分支中用类型断言告诉TypeScript当前返回值的类型:
interface Machine { doSomething: (arg: number) => string; doSomethingElse: (args: boolean) => number; }; type MachineParameters = { [K in keyof Machine]: [K, ...Parameters<Machine[K]>]; }[keyof Machine]; const execute = <T extends MachineParameters>(...args: T): ReturnType<Machine[T[0]]> => { switch (args[0]) { case "doSomething": return `${args[1]}` as ReturnType<Machine[T[0]]>; case "doSomethingElse": return Number(args[1]) as ReturnType<Machine[T[0]]>; default: throw new Error("Unknown operation"); } }; // 调用正常 const x = execute("doSomething", 1); const y = execute("doSomethingElse", true);
方法三:重构为非泛型函数,利用联合类型自动收窄
将函数参数直接定义为MachineParameters联合类型,TypeScript会在每个switch分支中自动收窄参数和返回类型:
interface Machine { doSomething: (arg: number) => string; doSomethingElse: (args: boolean) => number; }; type MachineParameters = { [K in keyof Machine]: [K, ...Parameters<Machine[K]>]; }[keyof Machine]; // 定义返回类型工具 type MachineReturn<K extends keyof Machine> = ReturnType<Machine[K]>; const execute = (...args: MachineParameters): MachineReturn<typeof args[0]> => { switch (args[0]) { case "doSomething": // args自动收窄为["doSomething", number] return `${args[1]}`; case "doSomethingElse": // args自动收窄为["doSomethingElse", boolean] return Number(args[1]); default: throw new Error("Unknown operation"); } }; // 调用正常 const x = execute("doSomething", 1); const y = execute("doSomethingElse", true);
内容的提问来源于stack exchange,提问作者Andreas
相关产品推荐
相关产品推荐

