TypeScript:推断对象内部泛型对象的类型
嘿,这个问题我之前也碰到过,是TypeScript泛型推断里典型的循环上下文依赖问题,我来给你捋捋原因和解决办法~
首先先把你的代码贴出来方便大家理解:
// 你的类型定义 export type SchemaEntry<A extends object, B> = | Validator<B> | ((obj: A) => Validator<B>); export type Schema<A extends object> = { [k in keyof A]: SchemaEntry<A, A[k]> }; // 你的函数 function foo<A extends object>(f: Schema<A>): A { return undefined as any; } // 调用时遇到的问题 const a = foo({ b: new Validator<number>(), c: (o) => new Validator<number>() }); // 参数o的类型无法被正确推断
问题出在哪?
当你调用foo时,TypeScript需要先推断出泛型A的类型,才能知道Schema<A>里那个(o) => Validator<number>函数的参数o应该是什么类型。但尴尬的是,A的类型本身又依赖于这个函数返回的Validator<number>对应的属性类型——等于说“我要先知道A是什么,才能确定o的类型;但我要先确定o的类型,才能知道A是什么”,形成了一个推断循环,TypeScript直接卡壳了。
怎么解决?
给你几个实用的方案,按需选:
1. 最直接:显式指定泛型A
既然TypeScript自己推不出来,那我们直接告诉它A的类型就行,这样o的类型会自动匹配A:
const a = foo<{ b: number; c: number }>({ b: new Validator<number>(), c: (o) => new Validator<number>() // 现在o会被正确推断成{ b: number; c: number } });
2. 更优雅:用satisfies拆分定义
如果你不想在调用foo时写泛型,可以先单独定义schema,用satisfies关键字约束它的类型,这样TypeScript能提前把o的类型推断出来:
const mySchema = { b: new Validator<number>(), c: (o: { b: number; c: number }) => new Validator<number>() } satisfies Schema<{ b: number; c: number }>; const a = foo(mySchema);
3. 彻底自动:用辅助函数打破推断循环
如果想完全让TypeScript自动推断,不用手动写类型,可以加个辅助函数帮TypeScript分步完成推断:
// 还是用你原来的类型定义 export type SchemaEntry<A extends object, B> = | Validator<B> | ((obj: A) => Validator<B>); export type Schema<A extends object> = { [k in keyof A]: SchemaEntry<A, A[k]> }; // 加个辅助函数专门用来创建schema function createSchema<A extends object>(schema: Schema<A>): Schema<A> { return schema; } // 先创建schema,此时TypeScript会先推断出A的类型,再确定o的类型 const mySchema = createSchema({ b: new Validator<number>(), c: (o) => new Validator<number>() // o现在会被正确推断成{ b: number; c: number } }); const a = foo(mySchema);
这个方法的核心是通过createSchema把“推断A”和“传入foo”拆成两步,打破了之前的循环,TypeScript就能顺利推导了。
总结
本质上就是TypeScript的泛型推断还没法处理这种“对象类型和内部函数参数类型互相依赖”的循环场景,要么我们手动给它明确类型,要么通过拆分步骤帮它理清推断顺序,问题就解决啦~
内容的提问来源于stack exchange,提问作者Jomik
相关产品推荐
相关产品推荐

