如何利用TypeScript泛型获取类型名称,自动映射GraphQL生成类型与数据库类型
实现GraphQL类型到DB类型的自动映射
首先要明确一个关键点:TypeScript没有原生能力直接获取泛型参数的类型名称作为字符串字面量。这是因为TypeScript采用结构类型系统,编译时会擦除类型名称信息,而且相同结构的类型会被视为等价,无法通过类型本身区分它们的命名。
不过根据你描述的命名规则(TypeName → TypeNameDB),我们可以通过TypeScript的映射类型和条件推断来实现自动映射,不需要手动维护静态字典。下面是几种可行的方案:
方案1:反向映射自动推导
我们可以先创建一个从DB类型到原GraphQL类型的映射,再通过反向查找来匹配对应的DB类型:
// mapper.ts import * as Types from "./generated"; // 第一步:创建反向映射,提取DB类型对应的原类型名称 type DBToOriginalMap = { [K in keyof Types as K extends `${infer OriginalName}DB` ? OriginalName : never]: Types[K] }; // 第二步:实现Mapper泛型,通过原类型查找对应的DB类型 type Mapper<T> = { // 遍历反向映射的所有key,找到类型匹配的项,返回对应的DB类型 [OriginalName in keyof DBToOriginalMap]: DBToOriginalMap[OriginalName] extends T ? Types[`${OriginalName}DB`] : never }[keyof DBToOriginalMap]; // 使用示例 type PersonDBType = Mapper<Types.Person>; // 类型为PersonDB type SomeOtherDBType = Mapper<Types.SomeOther>; // 类型为SomeOtherDB
优缺点
- ✅ 无需手动维护映射关系,只要符合
TypeName和TypeNameDB的命名规则就自动生效 - ❌ 如果存在两个结构完全相同的GraphQL类型,会返回联合类型(不过这种情况在业务场景中应该很少见)
方案2:结合Codegen生成辅助映射类型
既然你已经在用graphql-codegen,可以通过自定义配置或插件,在生成generated.ts时同时生成一个类型映射表:
比如在codegen配置中添加一个自定义模板,让它生成:
// generated.ts(自动生成) type Person = { name: string; }; type PersonDB = { name: string; _id: string; }; type SomeOther = { id: number; }; type SomeOtherDB = { id: number; _id: string; }; // 自动生成的类型映射 type TypeToDBMap = { Person: PersonDB; SomeOther: SomeOtherDB; // 所有类型自动添加 };
然后在mapper.ts中就可以非常简洁地实现:
// mapper.ts import * as Types from "./generated"; type Mapper<T> = T extends Types[keyof Types.TypeToDBMap] ? Types.TypeToDBMap[keyof { [K in keyof Types.TypeToDBMap]: Types.TypeToDBMap[K] extends T ? K : never }] : never; // 使用示例 type PersonDBType = Mapper<Types.Person>; // 类型为PersonDB
优缺点
- ✅ 类型映射完全自动生成,不会出错,且结构清晰
- ✅ 即使有结构相同的类型,也能准确匹配(因为是基于命名的显式映射)
- ❌ 需要额外配置codegen来生成映射表,不过这可以通过自定义插件或模板轻松实现
为什么getName<T>无法实现?
如开头所说,TypeScript的结构类型系统不保存类型的名称信息。举个例子:
type Person = { name: string }; type User = { name: string };
在TypeScript看来,Person和User是完全等价的类型,没有任何区别——所以无法通过泛型getName<T>区分它们的名称。这也是为什么我们必须基于命名规则或显式映射来实现类型转换的原因。
内容的提问来源于stack exchange,提问作者Eliav Louski
相关产品推荐
相关产品推荐

