如何从Watermelon DB列对象数组构造TypeScript泛型类型?
如何从Watermelon DB列定义数组推导TypeScript类型
我有一组Watermelon DB的数据库表列定义数组:
const columns = [ { name: "created_at", type: "number", isOptional: true }, { name: "created_by", type: "string" }, { name: "is_corrupt", type: "boolean", isOptional: true }, ];
希望通过泛型将其转换为如下TypeScript类型:
type ExpectedInferredTypeFromColumns = { created_at: number | null; created_by: string; is_corrupt: boolean | null; };
我的尝试代码如下,但推导结果不符合预期,每个属性都变成了所有可能类型的联合:
type InferTypeFromColumns<T extends ReadonlyArray<Column>> = { [K in T extends ReadonlyArray<infer U> ? U extends { name: string } ? U["name"] : never : never]: T extends ReadonlyArray<infer U> ? U extends { type: "number"; isOptional: true } ? number | null : U extends { type: "number" } ? number : U extends { type: "string"; isOptional: true } ? string | null : U extends { type: "string" } ? string : U extends { type: "boolean"; isOptional: true } ? boolean | null : U extends { type: "boolean" } ? boolean : never : never; }; type MyInferredType = InferTypeFromColumns<typeof columns>; // 生成结果: // type MyInferredType = { // created_at: string | number | boolean | null; // created_by: string | number | boolean | null; // is_corrupt: string | number | boolean | null; // }
问题原因
当前写法在遍历键时,没有为每个键关联对应的列定义类型,而是对整个数组的所有可能类型做了联合判断,导致每个属性都被推断为所有类型的集合。
正确实现
需要针对数组中的每个元素单独处理,利用T[number]获取数组元素的联合类型,再通过映射类型将元素的name作为对象键,同时根据该元素的type和isOptional推导对应的值类型:
// 定义列的基础类型 type Column = { name: string; type: "number" | "string" | "boolean"; isOptional?: boolean; }; // 单个列对应的属性类型转换逻辑 type ColumnToProp<T extends Column> = T["isOptional"] extends true ? { number: number | null; string: string | null; boolean: boolean | null; }[T["type"]] : { number: number; string: string; boolean: boolean; }[T["type"]]; // 最终泛型类型 type InferTypeFromColumns<T extends ReadonlyArray<Column>> = { [Col in T[number] as Col["name"]]: ColumnToProp<Col>; }; // 注意:必须给数组加`as const`,让TypeScript推断出具体的字面量类型 const columns = [ { name: "created_at", type: "number", isOptional: true }, { name: "created_by", type: "string" }, { name: "is_corrupt", type: "boolean", isOptional: true }, ] as const; // 验证推导结果 type MyInferredType = InferTypeFromColumns<typeof columns>; // 结果与预期完全一致: // type MyInferredType = { // created_at: number | null; // created_by: string; // is_corrupt: boolean | null; // }
关键细节
as const断言:如果不加这个断言,TypeScript会把type字段推断为宽泛的string类型,而非具体的"number"/"string"/"boolean"字面量,导致无法正确映射类型。- 映射类型的
as子句:[Col in T[number] as Col["name"]]实现了将数组元素的name提取为对象的键,同时保留每个元素的完整类型信息。 ColumnToProp类型:把类型转换逻辑抽离成单独的工具类型,避免了重复的条件判断,让代码更简洁易维护。
内容的提问来源于stack exchange,提问作者david_adler
相关产品推荐
相关产品推荐

