如何在TypeScript中实现DeepPick类型?
实现TypeScript的DeepPick类型
要实现支持深度选取嵌套属性的DeepPick类型,核心是利用TypeScript的模板字面量类型、递归类型和条件类型来解析嵌套路径,并处理对象、数组等不同场景。以下是完整实现方案:
步骤1:拆分路径字符串为元组
首先定义一个辅助类型SplitPath,把类似'books.name'的嵌套路径拆分成字符串元组['books', 'name'],方便后续递归处理:
type SplitPath<T extends string> = T extends `${infer Head}.${infer Tail}` ? [Head, ...SplitPath<Tail>] : [T];
步骤2:实现单一路径的深度选取
接下来实现DeepPickSingle,处理单个路径的深层属性选取,同时兼容数组类型的场景:
type DeepPickSingle<T, Path extends string> = SplitPath<Path> extends [infer Key extends keyof T, ...infer Rest] // 如果是路径最后一段,直接选取该属性 ? Rest extends [] ? { [K in Key]: T[K] } // 如果当前属性是数组,对数组元素递归应用DeepPickSingle : T[Key] extends Array<infer U> ? { [K in Key]: Array<DeepPickSingle<U, Rest[number] extends string ? Rest[number] : never>> } // 如果当前属性是对象,递归处理剩余路径 : T[Key] extends object ? { [K in Key]: DeepPickSingle<T[Key], Rest[number] extends string ? Rest[number] : never> } : never : never;
步骤3:合并多路径的选取结果
最后实现DeepPick,处理多个路径的联合类型,把每个路径的选取结果合并成一个完整类型:
// 辅助类型:合并交叉类型为普通对象类型 type Merge<T> = { [K in keyof T]: T[K] }; type DeepPick<T, Paths extends string> = Merge< { [P in Paths]: DeepPickSingle<T, P>; }[Paths] >;
测试验证
用你提供的示例代码测试,完全符合预期:
interface TestBook { id: string; name: string; } interface TestUser { id: string; email: string; books: TestBook[]; } const foo: DeepPick<TestUser, 'id' | 'books.name'> = { id: 'user-1', books: [{ name: 'TypeScript实战' }] }; // foo的类型为:{ id: string; books: Array<{ name: string }> }
关键技术点说明
- 模板字面量类型:通过
${infer Head}.${infer Tail}语法拆分嵌套路径字符串,实现路径的逐层解析。 - 递归类型:通过递归调用
DeepPickSingle处理深层嵌套的属性,直到路径的最后一段。 - 条件类型:区分当前属性是普通对象还是数组,分别处理数组元素的深层选取,保证类型兼容性。
- 类型合并:用
Merge辅助类型把多个路径生成的交叉类型转换为普通对象类型,避免类型显示异常。
扩展场景支持
这个实现还能处理可选属性、多层嵌套对象等场景:
interface TestAuthor { name: string; address?: { city: string; zip: number }; } interface TestBookWithAuthor extends TestBook { author: TestAuthor; } interface TestUserWithAuthor extends TestUser { favoriteBook: TestBookWithAuthor; } const bar: DeepPick<TestUserWithAuthor, 'favoriteBook.author.address.city' | 'books.id'> = { favoriteBook: { author: { address: { city: '上海' } } }, books: [{ id: 'book-1' }] }; // bar的类型为:{ favoriteBook: { author: { address: { city: string | undefined } } }; books: Array<{ id: string }> }
内容的提问来源于stack exchange,提问作者teteyi3241
相关产品推荐
相关产品推荐

