You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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;
// }

关键细节

  1. as const断言:如果不加这个断言,TypeScript会把type字段推断为宽泛的string类型,而非具体的"number"/"string"/"boolean"字面量,导致无法正确映射类型。
  2. 映射类型的as子句:[Col in T[number] as Col["name"]]实现了将数组元素的name提取为对象的键,同时保留每个元素的完整类型信息。
  3. ColumnToProp类型:把类型转换逻辑抽离成单独的工具类型,避免了重复的条件判断,让代码更简洁易维护。

内容的提问来源于stack exchange,提问作者david_adler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:54:19