如何在TypeScript中根据值类型提取键并约束函数入参?
问题场景
我们先定义如下的Test接口及对应实例:
interface Test { hello: () => 'Hello World!' test: 1 } const test: Test = { hello () { return 'Hello World!' }, test: 1 }
我们可以实现一个根据键获取对象值的通用函数:
function getTestValue<Key extends keyof Test> (key: Key): Test[Key] { return test[key] }
现在需要实现一个仅能获取值为函数类型的键的函数。如果用运行时判断非函数值就抛错的方式,属于临时方案:
function getTestMethod<Key extends keyof Test> (key: Key): Test[Key] { const method = test[key] if (typeof method !== 'function') throw new Error('Method is not a function') return method }
这种写法的问题是,IntelliSense会提示Test的所有键(包括非函数类型的test),很容易引发bug,这正是我们想用TypeScript类型系统提前规避的问题。
那怎么通过TypeScript类型系统实现仅允许传入值为函数的键?
解决方案
可以通过条件类型筛选出Test中值为函数类型的键,具体实现如下:
1. 定义工具类型提取函数键
先写一个通用工具类型,用来提取任意对象类型中值为函数的键:
type FunctionKeys<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]
这个类型的逻辑是:遍历对象T的所有键K,判断T[K]是否为函数类型;如果是,就保留键名K,否则返回never;最后通过[keyof T]取出所有有效键的联合类型。
2. 用工具类型约束函数参数
将上述工具类型应用到目标函数的泛型约束中:
function getTestMethod<Key extends FunctionKeys<Test>>(key: Key): Test[Key] { return test[key] }
效果验证
现在使用getTestMethod时,IntelliSense只会提示符合要求的键(比如这里的hello);如果传入非函数类型的键(比如test),TypeScript会直接在编译阶段报错:
getTestMethod('hello') // 正常,返回类型为 () => 'Hello World!' getTestMethod('test') // 编译报错:类型'"test"'的参数不能赋给类型'"hello"'的参数
这种方式完全通过类型系统实现了需求,不需要依赖运行时判断,从根源上避免了误传键名的问题。
内容的提问来源于stack exchange,提问作者Dinhero21
相关产品推荐
相关产品推荐

