如何基于传入元组收窄TypeScript模板字面量生成的联合类型?
问题描述
我有一个通过模板字面量类型生成的联合类型,该模板字面量基于包含两个字符串的联合类型构建,会生成所有可能的排列组合。现在这个类型是通过一个接收元组数组的泛型函数定义的,请问能不能根据传入函数的元组值来收窄这个联合类型,只保留元组对应的组合?
示例代码:
function get<T extends string, K extends string>(vals: [T, K][]) { type FullKey = `${T}${K}` const answer: FullKey[] = [] for(const v of vals) { const fullKey: FullKey = `${v[0]}${v[1]}` answer.push(fullKey) } return answer } const answer = get([['1', 'a'], ['2', 'b']]) // ^? // 如何让它的类型变成 ('1a' | '2b')[] 而不是全部排列组合的联合?
解决方案
要实现这个需求,核心是让TypeScript追踪数组中每个元组的具体配对关系,而非将所有T和K的组合无差别枚举。下面提供两种可行的实现方式:
方法一:元组数组泛型 + 映射类型
修改函数泛型定义,让它捕获整个输入数组的元组结构,再通过映射类型生成精确的拼接结果:
function get<T extends [string, string][]>(vals: [...T]) { // 遍历数组每个索引,拼接对应元组的两个元素 type FullKey = { [I in keyof T]: `${T[I][0]}${T[I][1]}` }[number] const answer: FullKey[] = [] for (const v of vals) { answer.push(`${v[0]}${v[1]}` as FullKey) } return answer } const answer = get([['1', 'a'], ['2', 'b']]) // answer 的类型自动推断为 ('1a' | '2b')[]
关键细节
T extends [string, string][]:让泛型捕获输入数组的具体元组结构(比如[["1","a"], ["2","b"]]),而非模糊的[string, string][]。[...T]:强制TypeScript将输入推断为元组类型,保留每个元素的具体值信息,避免被推断为普通数组。- 映射类型
{ [I in keyof T]: ... }[number]:遍历元组的每个索引,将对应位置的元组元素拼接成模板字面量,最后提取所有拼接结果的联合类型。
方法二:分布式条件类型(适配复杂场景)
如果需要处理嵌套或动态生成的元组数组,可以用分布式条件类型逐个处理每个元组:
// 将单个元组转换为拼接字符串类型 type TupleToKey<T extends [string, string]> = `${T[0]}${T[1]}` // 将元组数组转换为所有拼接结果的联合类型 type ArrayToKeys<T extends [string, string][]> = T extends Array<infer U> ? U extends [string, string] ? TupleToKey<U> : never : never function get<T extends [string, string][]>(vals: [...T]): ArrayToKeys<T>[] { return vals.map(v => `${v[0]}${v[1]}` as ArrayToKeys<T>) } const answer = get([['1', 'a'], ['2', 'b']]) // answer 的类型为 ('1a' | '2b')[]
原理说明
分布式条件类型会自动遍历数组中的每个元组类型U,将其传入TupleToKey转换为对应的拼接字符串,最终汇总成所有合法配对的联合类型,完美匹配输入元组的实际组合。
内容的提问来源于stack exchange,提问作者pekala
相关产品推荐
相关产品推荐

