TypeScript:如何让内部函数参数推断为子对象键类型
问题描述
尝试基于react-hook-form的register函数封装包装函数时,遇到类型兼容问题,核心是如何约束内部函数的参数类型。示例代码如下:
interface Child1 { p1: string; } interface Child2 { p2: number } interface Parent { child1: Child1; child2: Child2 } function register(o: "child1.p1" | "child2.p2") {return {}} function getChildRegister(parent: Parent, parentKey: keyof Parent) { return function(childKey: keyof Parent[typeof parentKey]) { return register(`${parentKey}.${childKey}`) } } const p: Parent = {child1: {p1: ""}, child2: {p2: 1}} const result = getChildRegister(p, "child1")("p1") // 报错!内部函数参数未正确推断
传入"child1"后,期望内部函数参数能接受Child1的键(即p1),但TypeScript仅允许传入string类型,需要让内部函数正确推断子对象的键类型。
解决方案
问题根源在于getChildRegister未通过泛型固定parentKey的具体类型,导致TypeScript无法在返回的内部函数中精确推导子对象的键类型。通过添加泛型参数约束parentKey的类型即可解决:
interface Child1 { p1: string; } interface Child2 { p2: number } interface Parent { child1: Child1; child2: Child2 } function register(o: "child1.p1" | "child2.p2") {return {}} // 添加泛型K,约束为keyof Parent的子集 function getChildRegister<K extends keyof Parent>(parent: Parent, parentKey: K) { // 此时Parent[K]为对应子对象的精确类型,keyof Parent[K]即子对象的键集合 return function(childKey: keyof Parent[K]) { // 用模板字面量类型做类型断言,让TypeScript识别拼接后的字符串符合register的参数要求 return register(`${parentKey}.${childKey}` as `${K}.${keyof Parent[K]}`) } } const p: Parent = {child1: {p1: ""}, child2: {p2: 1}} const result = getChildRegister(p, "child1")("p1") // 正常工作,参数仅接受"p1"
关键说明:
- 泛型
K extends keyof Parent固定了parentKey的具体类型,让TypeScript能追踪到对应子对象的类型 - 内部函数的
childKey类型设为keyof Parent[K],自动匹配当前子对象的所有键 - 模板字面量类型断言确保拼接后的参数与
register的联合类型参数兼容
内容的提问来源于stack exchange,提问作者REDDY PRASAD
相关产品推荐
相关产品推荐

