TypeScript:通过对象字面量映射重命名类型字段(支持嵌套)
问题与解决方案
问题背景
现有Person接口定义如下:
interface Person { id: string; name: string; dob: Date; dod?: Date; nicknames?: string[]; age: number; height: number; weight: number; }
需要实现泛型函数getThing,接收目标类型(如Person)和字段映射对象(支持重命名),返回对应新类型。示例调用后,返回值类型需符合预期:
const a = getThing<Person>({ id: "id" as const, name: "name" as const, dateOfBirth: "dob" as const, }); // 期望a的类型:{ id: string; name: string; dateOfBirth: Date; }
尝试的代码出现R[k] cannot be used to index T错误,同时希望扩展支持嵌套字段重命名(如将Person.contact.homePhone重命名为ice.phone),用于构建类型化GraphQL查询工具。
一、解决基础字段重命名的类型错误
原代码的核心问题是对泛型R的约束不足,TypeScript无法确认R[k]必然是T的键。只需给R添加明确约束即可修复:
function getThing<T, R extends Record<keyof R, keyof T>>( fields: R ): { [K in keyof R]: T[R[K]] } { // 业务逻辑实现,此处用类型断言占位 return {} as any; }
优化调用体验(TS 4.5+)
如果升级到TypeScript 4.5及以上版本,可利用自动字面量类型推断,无需手动添加as const:
const a = getThing<Person>({ id: "id", name: "name", dateOfBirth: "dob", }); // 自动推断a的类型为 { id: string; name: string; dateOfBirth: Date; }
二、扩展支持嵌套字段重命名
要实现嵌套字段的映射,需要定义递归类型来处理层级结构:
1. 定义辅助类型
首先定义描述嵌套映射结构的类型,以及递归解析的返回类型:
// 描述支持嵌套的字段映射规则 type FieldMapping<T> = { [K: string]: keyof T | FieldMapping<T[keyof T]>; }; // 递归生成最终的返回类型 type MappedResult<T, M extends FieldMapping<T>> = { [K in keyof M]: M[K] extends keyof T ? T[M[K]] : M[K] extends FieldMapping<infer U> ? MappedResult<U, M[K]> : never; };
2. 修改泛型函数
更新getThing的类型约束,适配嵌套场景:
function getThing<T, M extends FieldMapping<T>>( fields: M ): MappedResult<T, M> { // 嵌套字段的实现需递归遍历映射对象,此处用类型断言占位 return {} as any; }
3. 嵌套场景使用示例
先扩展Person接口添加嵌套字段:
interface ContactInfo { homePhone: string; email: string; } interface Person { id: string; name: string; dob: Date; dod?: Date; nicknames?: string[]; age: number; height: number; weight: number; contact: ContactInfo; }
调用getThing传入嵌套映射:
const b = getThing<Person>({ id: "id", name: "name", dateOfBirth: "dob", ice: { phone: "homePhone" } });
此时b的类型自动推断为:
{ id: string; name: string; dateOfBirth: Date; ice: { phone: string }; }
版本要求
嵌套方案依赖TypeScript 4.1+的递归条件类型支持,当前使用的4.3.5版本完全兼容,无需升级。
内容的提问来源于stack exchange,提问作者Liandri
相关产品推荐
相关产品推荐

