TypeScript:如何为合并Holder实例的combineHolders函数正确标注类型
如何为combineHolders函数正确标注TypeScript类型并支持嵌套合并
当然可以!我们可以通过TypeScript的条件类型、递归类型和泛型来完美解决这个问题,不仅能正确标注combineHolders函数的类型,还能自然支持嵌套结构的合并。下面一步步来实现:
1. 定义基础Holder类及示例
首先我们先明确Holder类的结构(假设它持有一个泛型值),并创建一些实例和嵌套的HoldersObject:
class Holder<T> { constructor(public value: T) {} } // 单个Holder实例 const nameHolder = new Holder<string>("Alice"); const ageHolder = new Holder<number>(30); // 嵌套的HoldersObject const userHolders = { name: nameHolder, age: ageHolder, address: { street: new Holder<string>("Main St"), city: new Holder<string>("New York") } };
2. 实现ExtractGeneric工具类型
我们需要一个工具类型来提取Holder<T>中的泛型参数T,这是后续合并类型的基础:
// 从Holder实例中提取它持有的值类型 type ExtractGeneric<H> = H extends Holder<infer T> ? T : never;
这个类型利用TypeScript的条件类型+infer关键字,自动推断并提取Holder的泛型参数。
3. 实现递归的CombineHolders类型
接下来需要一个递归类型,用来将嵌套的HoldersObject转换为合并后的目标类型——它会遍历对象的每个键,递归处理嵌套结构:
// 递归处理HoldersObject,将每个Holder替换为对应的值类型 type CombineHolders<T> = { [K in keyof T]: T[K] extends Holder<any> ? ExtractGeneric<T[K]> : T[K] extends object ? CombineHolders<T[K]> : T[K] };
- 如果当前值是
Holder实例,就用ExtractGeneric提取它的值类型; - 如果当前值是普通对象(非Holder),就递归调用
CombineHolders处理这个嵌套对象; - 其他类型则保持原样。
4. 为combineHolders函数标注类型并实现逻辑
现在可以给combineHolders函数添加泛型标注,同时实现递归合并的逻辑:
function combineHolders<T extends Record<string, any>>(holders: T): Holder<CombineHolders<T>> { const combined: Record<string, any> = {}; for (const key in holders) { const current = holders[key]; if (current instanceof Holder) { // 如果是Holder实例,直接提取value combined[key] = current.value; } else if (typeof current === 'object' && current !== null) { // 如果是嵌套对象,递归合并后提取value combined[key] = combineHolders(current).value; } else { // 非对象非Holder的类型直接保留 combined[key] = current; } } return new Holder(combined); }
函数的泛型T约束为键值对对象,返回类型是Holder<CombineHolders<T>>——TypeScript会自动根据输入的HoldersObject推断出合并后的精确类型。
测试效果
现在我们可以测试一下,TypeScript会完美推断出合并后的类型,包括嵌套结构:
const combinedUser = combineHolders(userHolders); // TypeScript能正确推断出: // combinedUser: Holder<{ // name: string; // age: number; // address: { // street: string; // city: string; // } // }> console.log(combinedUser.value.name); // 类型推断为string console.log(combinedUser.value.address.city); // 类型推断为string
这样不仅完成了类型标注,嵌套结构的合并也自然实现了——递归类型和递归函数逻辑完美匹配,保证了类型安全和运行时行为一致。
内容的提问来源于stack exchange,提问作者aleclofabbro
相关产品推荐
相关产品推荐

