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

如何用TypeScript泛型推断数组转对象类型并校验长度与唯一键?

TypeScript泛型实现键值数组转对象(含长度与键唯一性约束)

核心需求回顾

我们需要实现一个类似_.zipObject的函数,将两个数组分别作为键和值生成对应类型的对象,同时满足:

  • 自动推断对象的键值类型,优先支持位置对应精确类型
  • 强制约束键数组与值数组长度一致
  • 强制约束键数组中的键唯一

1. 实现精确的键值类型推断

要让TypeScript根据输入的键值元组,推断出每个键对应对应位置值类型的对象,我们可以利用元组索引映射实现:

// 核心转换类型:将键元组与值元组映射为对象类型
type ZipObject<K extends readonly string[], V extends readonly any[]> = {
  [I in keyof K as K[I] extends string ? K[I] : never]: V[I]
};

这个类型的逻辑是:遍历键元组的每个索引I,将索引对应的键K[I]作为对象的属性名,将值元组对应索引的类型V[I]作为属性值类型,最终生成精确匹配的对象类型。


2. 约束键值数组长度一致

通过泛型约束让K和V的长度属性完全相等,就能强制要求输入的两个数组长度相同:

K extends readonly string[]
// 约束长度一致:值数组长度必须等于键数组长度
V extends readonly any[] & { length: K['length'] }

这里利用TypeScript的元组长度字面量特性,只有当两个元组长度完全相同时,V['length']才会匹配K['length']。


3. 约束键数组无重复元素

我们需要自定义一个类型工具来检查元组中是否存在重复字符串,再通过泛型约束拒绝存在重复键的数组:

// 检查元组中是否存在重复键的类型工具
type DuplicateKeys<T extends readonly string[]> = T extends [infer First, ...infer Rest]
  ? Rest extends readonly string[]
    ? First extends Rest[number]
      ? First // 存在重复,返回重复的键
      : DuplicateKeys<Rest> // 递归检查剩余元素
    : never
  : never; // 无重复,返回never

然后在泛型约束中添加:DuplicateKeys<K> extends never,这样当键数组存在重复时,TypeScript会直接报错。


完整实现与测试

将以上逻辑整合到函数中,最终实现如下:

// 检查元组中是否存在重复键的类型工具
type DuplicateKeys<T extends readonly string[]> = T extends [infer First, ...infer Rest]
  ? Rest extends readonly string[]
    ? First extends Rest[number]
      ? First
      : DuplicateKeys<Rest>
    : never
  : never;

// 核心键值转对象类型
type ZipObject<K extends readonly string[], V extends readonly any[]> = {
  [I in keyof K as K[I] extends string ? K[I] : never]: V[I]
};

function objectBuilder<
  K extends readonly string[] & (DuplicateKeys<K> extends never ? K : never),
  V extends readonly any[] & { length: K['length'] }
>(keyArr: K, valArr: V): ZipObject<K, V> {
  const result = {} as ZipObject<K, V>;
  keyArr.forEach((key, i) => {
    result[key] = valArr[i] as ZipObject<K, V>[typeof key];
  });
  return result;
}

测试示例

// 合法:键值长度一致,键唯一,类型推断精确
const obj1 = objectBuilder(['foo', 'bar', 'baz'], [11, 22, 33]);
// obj1 类型:{ foo: number; bar: number; baz: number; }

// 合法:混合值类型,推断精确对应类型
const obj2 = objectBuilder(['foo', 'bar', 'baz'], [11, "22", false]);
// obj2 类型:{ foo: number; bar: string; baz: boolean; }

// 报错:值数组长度不足
objectBuilder(['foo', 'bar', 'baz'], [11, 22]);

// 报错:值数组长度过长
objectBuilder(['foo', 'bar'], [11, 22, 33]);

// 报错:键数组存在重复的'foo'
objectBuilder(['foo', 'bar', 'foo'], [11, 22, 33]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:43:16