使用TypeScript泛型实现无重复字符串消费类型
TypeScript泛型实现无重复全排列数组类型限制
需求说明
需要实现一种TypeScript泛型类型,用于约束字符串数组满足以下要求:
- 必须包含指定联合类型的所有字符串值
- 数组中不能有重复元素
- 支持元素的任意排列(如
["month", "day", "year"]是合法的)
比如对于联合类型"year" | "month" | "day",普通的Array<"year" | "month" | "day">无法阻止重复值(如["year", "year", "year"])或缺少元素的情况,需要通过泛型解决这个问题。
实现方案
我们可以利用TypeScript的递归泛型和条件类型,跟踪已使用的元素,确保数组包含所有目标元素且无重复:
1. 辅助类型:获取联合类型的元素数量
首先需要一个辅助类型来计算联合类型的元素个数,用于后续判断是否已包含所有元素:
type LengthOfUnion<T, Accumulator extends any[] = []> = T extends never ? Accumulator['length'] : LengthOfUnion<Exclude<T, T>, [...Accumulator, T]>;
2. 核心泛型类型:UniqueFullTuple
定义核心泛型UniqueFullTuple,通过递归逐步"消费"联合类型中的元素,确保无重复且全量使用:
type UniqueFullTuple<T extends string, Used extends string[] = []> = // 当已使用元素的联合类型等于目标T时,返回当前数组类型 Used[number] extends T ? Used : // 否则,仅允许选择T中未被使用的元素,递归构建数组 { [K in T]: [K, ...UniqueFullTuple<Exclude<T, K>, [...Used, K]>] }[T];
3. 使用示例
// 定义目标联合类型 type DateParts = "year" | "month" | "day"; // 合法的全排列数组 const validOrder1: UniqueFullTuple<DateParts> = ["year", "month", "day"]; const validOrder2: UniqueFullTuple<DateParts> = ["month", "day", "year"]; const validOrder3: UniqueFullTuple<DateParts> = ["day", "year", "month"]; // 以下情况会触发TypeScript类型错误: const duplicateError: UniqueFullTuple<DateParts> = ["year", "year", "day"]; // 存在重复元素 const missingError: UniqueFullTuple<DateParts> = ["year", "month"]; // 缺少元素 const extraError: UniqueFullTuple<DateParts> = ["year", "month", "day", "hour"]; // 包含多余元素
原理说明
- 递归逻辑:每次从联合类型
T中选择一个未被使用的元素K,通过Exclude<T, K>移除已选元素,将K加入已使用数组Used,继续递归处理剩余元素。 - 终止条件:当
Used[number](已使用元素的联合类型)等于目标T时,说明所有元素都已被使用,返回当前的数组类型。 - 类型约束:通过索引类型
{ [K in T]: ... }[T],确保只能选择T中未被使用的元素,从而避免重复。
内容的提问来源于stack exchange,提问作者cyan-kinesin
相关产品推荐
相关产品推荐

