如何为多模型派生的列定义映射获取正确的TypeScript类型标注
问题根因
- 使用交叉类型
User & Book & UserUID时,三个模型中同名的id字段类型分别是number、number、string,TypeScript 对交叉类型的同名字段会做类型交叉运算,number & string的结果是never,所以会出现id字段推导为 never 的问题。 - 直接使用三个模型的联合类型时,
keyof 联合类型只能获取到所有模型共有的属性,因此除了所有模型都有的id字段外,其他字段都无法被识别。
解决思路
实现工具类型,把多个模型的所有字段汇总,同名字段的类型取所有模型中该字段类型的联合而非交叉:
- 把所有用到的模型汇总为一个联合类型
- 提取所有模型的所有字段名组成联合
- 对每个字段名,收集所有模型中该字段的类型组成联合
- 基于汇总后的类型定义列映射
完整可运行代码如下:
export type User = { id: number; admin: boolean; email: string; }; export type Book = { id: number; title: string; author: string; userId: number; }; export type UserUID = { id: string; admin: boolean; email: string; }; // 汇总所有模型为联合类型 type AllModels = User | Book | UserUID; // 工具类型:提取所有模型的所有字段名 type AllModelKeys<T> = T extends any ? keyof T : never; // 工具类型:提取指定字段在所有模型中的类型联合 type GetFieldType<T, K extends PropertyKey> = T extends Record<K, infer V> ? V : never; // 汇总后的字段类型:同名字段类型取联合 type MergedDataRow<T> = { [K in AllModelKeys<T>]: GetFieldType<T, K> }; // 基于汇总类型定义列配置 export interface ColumnDefinition<F extends keyof MergedDataRow<AllModels>> { valueFormatter?: (value: MergedDataRow<AllModels>[F]) => string; } // 字段后加?表示允许只配置部分字段,需要强制全量配置可删除? type ColumnDefinitionMap = { [key in keyof MergedDataRow<AllModels>]?: ColumnDefinition<key>; }; const columnDefinitions: ColumnDefinitionMap = { admin: { // 类型正常推导为 boolean valueFormatter: (value) => (value === true ? 'Admin' : 'User'), }, id: { // 类型正常推导为 number | string valueFormatter: (value) => typeof value === 'string' ? value : value.toString(), }, // 其他非共有字段也可以正常识别 title: { valueFormatter: (value) => `《${value}》` } };
内容的提问来源于stack exchange,提问作者nvitaterna
相关产品推荐
相关产品推荐

