如何为主题色对象定义元组首尾元素唯一的TypeScript类型?
TypeScript 主题色类型约束:确保元组元素唯一性
需求背景
现有主题色对象定义如下:
themeColors = { primary: ['#fff', '#000'], secondary: ['#DDD', '#333'], error: ['#CCC', '#444'] }
当前基础类型为:
themeColor: Record<string, [string, string]>
需要给这个类型添加严格约束:所有元组的第一个元素必须唯一,第二个元素也必须唯一。以下两种情况应触发类型检查错误:
错误示例1:第一个元素重复
themeColors = { primary: ['#fff', '#000'], secondary: ['#fff', '#333'], }
(#fff作为元组第一个元素重复出现)
错误示例2:第二个元素重复
themeColors = { primary: ['#fff', '#000'], secondary: ['#DDD', '#000'] }
(#000作为元组第二个元素重复出现)
实现方案
我们可以通过TypeScript的条件类型、递归类型和映射类型来实现这一约束,核心思路是提取所有元组的对应元素并检查唯一性:
1. 定义工具类型:提取元组元素
首先定义两个工具类型,分别提取主题色对象中所有元组的第一个和第二个元素:
// 提取所有元组的第一个元素组成元组类型 type TupleFirsts<T extends Record<string, [string, string]>> = { [K in keyof T]: T[K][0] }[keyof T][] // 提取所有元组的第二个元素组成元组类型 type TupleSeconds<T extends Record<string, [string, string]>> = { [K in keyof T]: T[K][1] }[keyof T][]
2. 定义工具类型:检查数组元素唯一性
通过递归方式检查数组中的元素是否唯一,如果存在重复元素则返回never:
type IsUniqueArray<T extends readonly any[]> = T extends [infer First, ...infer Rest] ? First extends Rest[number] ? never : IsUniqueArray<Rest> : T
3. 定义最终主题色类型
将上述工具类型组合,约束主题色对象的两个元组元素集合都必须唯一:
type ThemeColor<T extends Record<string, [string, string]>> = IsUniqueArray<TupleFirsts<T>> extends never ? never : IsUniqueArray<TupleSeconds<T>> extends never ? never : T
4. 使用示例
// 合法:所有元组元素均唯一 const validTheme: ThemeColor<{ primary: ['#fff', '#000'], secondary: ['#DDD', '#333'], error: ['#CCC', '#444'] }> = { primary: ['#fff', '#000'], secondary: ['#DDD', '#333'], error: ['#CCC', '#444'] } // 不合法:第一个元素重复,触发类型错误 const invalidTheme1: ThemeColor<{ primary: ['#fff', '#000'], secondary: ['#fff', '#333'], }> = { primary: ['#fff', '#000'], secondary: ['#fff', '#333'], } // 不合法:第二个元素重复,触发类型错误 const invalidTheme2: ThemeColor<{ primary: ['#fff', '#000'], secondary: ['#DDD', '#000'] }> = { primary: ['#fff', '#000'], secondary: ['#DDD', '#000'] }
实现说明
TupleFirsts和TupleSeconds通过映射类型遍历主题色对象的所有键,提取对应元组的指定元素并组成新的元组类型;IsUniqueArray递归检查数组中的每个元素,如果当前元素存在于剩余元素中,就返回never标记为不合法;ThemeColor类型会分别检查两个元素集合的唯一性,只要有一个集合存在重复,就返回never,从而触发TypeScript的类型检查错误。
内容的提问来源于stack exchange,提问作者Михаил Бобрышев
相关产品推荐
相关产品推荐

