TypeScript中如何实现方法返回值类型的泛型映射?
解决TypeScript泛型筛选方法并映射返回值类型的问题
问题场景
现有如下接口与类型定义:
interface X { a: () => number; b: number; } type MethodsOf<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; }[keyof T];
MethodsOf<T>的作用是筛选出对象类型中值为函数的键,比如:
type A = MethodsOf<X>; // 类型为 "a" const a: A = 'a'; // 合法 const b: A = 'b'; // 报错:Type '"b"' is not assignable to type '"a"'
需要实现一个泛型类型MockMapping<T>(或ValuesMapping<T>),要求:
- 仅包含
MethodsOf<T>筛选出的键 - 对应键的值类型为该方法返回值的
Mock包装类型(或直接返回值类型)
尝试的实现代码如下,但触发TS2344错误:
type MockMapping<T> = { [K in MethodsOf<T>]: Mock<ReturnType<T[K]>>; }; // 使用示例 class X { a() { return 1; } b = 2; } const x: MockMapping<X> = { a: jest.fn(() => 42), };
错误提示:Type 'T[K]' does not satisfy the constraint '(...args: any) => any',原因是TypeScript无法自动推断T[K]必然是函数类型。
解决方案
方案一:使用键重映射直接筛选(推荐)
不需要单独依赖MethodsOf<T>,直接在映射类型中通过as子句筛选函数键,同时让编译器明确T[K]的函数类型:
type MockMapping<T> = { // 仅保留值为函数的键,并映射到对应返回值的Mock类型 [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: Mock<ReturnType<T[K]>>; };
方案二:保留MethodsOf并添加类型断言
如果需要继续使用MethodsOf<T>,可以通过类型断言告诉编译器T[K]是函数类型:
type MethodsOf<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; }[keyof T]; type MockMapping<T> = { [K in MethodsOf<T>]: Mock<ReturnType<T[K] as (...args: any[]) => any>>; };
验证效果
用示例类X测试:
class X { a() { return 1; } b = 2; } // 合法:符合类型要求 const x: MockMapping<X> = { a: jest.fn(() => 42), }; // 报错:返回值类型不匹配(string不能赋值给number的Mock类型) const x2: MockMapping<X> = { a: jest.fn(() => 'blarg'), }; // 报错:不存在的键'b'不允许出现在类型中 const x3: MockMapping<X> = { a: jest.fn(() => 42), b: jest.fn(() => 5), };
原理说明
TypeScript处理泛型映射时,仅通过MethodsOf<T>筛选键的话,编译器无法反向确认T[K]一定是函数类型(泛型T可能存在联合类型等边缘场景)。而使用**键重映射(as子句)**的方式,在遍历键的过程中直接完成筛选,编译器能明确当前K对应的T[K]是函数类型,因此可以安全调用ReturnType获取返回值类型。
内容的提问来源于stack exchange,提问作者vlio20
相关产品推荐
相关产品推荐

