如何为指定键值唯一的对象数组创建TypeScript类型?
如何为指定键值唯一的对象数组创建TypeScript类型?
嘿,这个需求挺实用的!我刚好研究过类似的场景,TypeScript的类型系统虽然是静态的,但通过一些递归类型技巧,咱们可以在编译时就帮你守住“指定键值唯一”这条规则。
先直接上核心的类型实现,咱们再慢慢拆解:
// 定义一个泛型类型,T是数组类型,K是要检查的键名 type UniqueByKey<T extends readonly any[], K extends keyof T[number]> = T extends [infer First, ...infer Rest] ? // 检查当前第一个元素的K键值,是否在剩余元素的K键值里出现过 First[K] extends Rest[number][K] ? never // 如果重复,就返回never,让类型报错 : [First, ...UniqueByKey<Rest, K>] // 如果不重复,继续递归检查剩余元素 : T; // 空数组或者递归到末尾,直接返回原类型
这个类型的思路很简单:用递归的方式逐个遍历数组(这里得用元组类型,因为普通的Array<T>是不确定长度的,TypeScript没法逐个检查),每次检查当前元素的指定键值,有没有在后面的元素里重复出现。如果有重复,整个类型就会变成never,赋值的时候TypeScript就会直接报错。
咱们来测试你给出的例子:
// 合法的情况,完全没问题 const validArr1: UniqueByKey<[{ a: 1 }, { a: 2 }, { a: 3 }], 'a'> = [{ a: 1 }, { a: 2 }, { a: 3 }]; // ✅ const validArr2: UniqueByKey<[{ a: true }, { a: "nice" }, { a: 3 }], 'a'> = [{ a: true }, { a: "nice" }, { a: 3 }]; // ✅ // 不合法的情况,TypeScript会直接提示类型不匹配 const invalidArr1: UniqueByKey<[{ a: 1 }, { a: 1 }, { a: 3 }], 'a'> = [{ a: 1 }, { a: 1 }, { a: 3 }]; // ❌ const invalidArr2: UniqueByKey<[{ a: true }, { a: "nice" }, { a: true }], 'a'> = [{ a: true }, { a: "nice" }, { a: true }]; // ❌
不过得提一句:这个方法只对固定长度的元组数组生效。如果是动态生成的数组(比如用Array<{a: number}>这种不确定长度的类型),TypeScript的类型系统没办法遍历所有可能的元素,也就没法做编译时检查。
这种情况下,咱们可以结合运行时校验来补全,写一个简单的函数来检查数组的键值唯一性,再配合类型守卫让TypeScript识别合法的数组:
function hasUniqueKeys<T, K extends keyof T>(arr: T[], key: K): arr is UniqueByKey<T[], K> { const valueSet = new Set(arr.map(item => item[key])); return valueSet.size === arr.length; } // 使用示例 const testArr = [{ a: 1 }, { a: 2 }, { a: 1 }]; if (hasUniqueKeys(testArr, 'a')) { // 这里testArr会被TypeScript推断为符合UniqueByKey的类型,可以放心使用 console.log('数组的a键值都是唯一的'); } else { console.error('数组里有重复的a键值!'); }
总的来说,编译时类型检查能帮你在写代码的时候提前发现问题,运行时校验则能处理动态生成的数组场景,两者结合起来就能完美满足你的需求啦~
备注:内容来源于stack exchange,提问作者Lars Flieger
相关产品推荐
相关产品推荐

