TypeScript:联合类型可以转化为元组类型吗?数组如何用联合类型逐项约束?

如何用联合类型约束数组类型

TypeScript 类型体操中,我希望能用联合类型约束数组(或元组)类型,实现更强的类型安全。本文将围绕这个主题展开,介绍常见的思路、局限与解决方案。

1. 联合类型与元组类型的关系

我们知道,TypeScript 可以很容易地将元组类型转化为联合类型:

typescript 复制代码
const tuple = ['a', 'b', 'c'] as const; // as const 不能遗漏哦
type Union = (typeof tuple)[number]; // 'a' | 'b' | 'c'

但反过来,在进行了一些徒劳的尝试和资料查询后发现:

无法直接将联合类型转化为元组类型(;´д`)ゞ。

因为联合类型本质上是无序的集合,而元组类型是有序的列表,TypeScript 类型系统无法保证顺序和长度。

2. 如何实现精准的类型约束

虽然不能直接将联合类型转为元组,但我们依然可以做到:

  • 让数组的元素类型受联合类型约束
  • 任何一方缺失都会在类型检查阶段暴露

3. 开始操作

假设这样一个场景,我们在做参数校验:

有一个如下的联合类型的入参。现在,我们要设定一个数组,使用includes方法来确定入参的有效性,但是入参类型可能会随着版本变化而变化,我们希望类型提示能帮助我们快速发现、更新。

typescript 复制代码
type InputVersion = 'latest' | 1 | 2;
  1. 先给出想要的数组,以as const约束它并提取元组类型
typescript 复制代码
const v = ['latest', 1, 2] as const;
type ArrVersion = (typeof v)[number]; // 'latest' | 1 | 2
  1. 对比类型ArrVersionInputVersion是否相同
typescript 复制代码
type What = IsSameType<ArrVersion, InputVersion>;
  1. 给一个变量显式声明此类型并赋值
typescript 复制代码
const what: What = 1; // 如果ArrVersion和InputVersion不一致,这行代码会因类型不匹配而报错。
// 严谨版本这里可以赋值为true,具体什么值都没有关系,选择自己喜欢的就好

Note: 这个赋值语句将会在编译期间被打包工具terser消除,因其未使用过。所以无需担心最终结果多出赋值语句。 (o゜▽゜)o

4. 实现 IsSameType 工具类型

直观版本

从数学理论讲,关键字extends类似于"偏序关系",因此对于偏序关系而言,只要a ≤ bb ≤ a同时成立,那么就可以得到a = b。因此,我们以三元运算符来做到这件事,一个直接的想法是这样:

typescript 复制代码
// 这是直观版本,但不是最优,最优请见下方的"严谨版本"
type IsSameTypeIntuitionistic<A, B> = A extends B ? (B extends A ? 1 : 2) : 2;

Note: 如果用truefalse,那么此泛型工具会永远返回boolean类型从而失去判断能力。
Note: 不使用10是因为0作为falsy的值性质略有区别,可能使得推断结果和约束行为不如预期。

严谨版本

TypeScript 存在"分布式展开"行为,当类型入参存在 never 时,分布式展开会直接返回 never,不会进入分支。以下是社区体操之神( (#°Д°)?)提供的严谨版本:

typescript 复制代码
// 严谨版本
type IsSameType<A, B> =
  (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;

此写法阻止了"分布式展开"行为,边界情况都可以照顾到,适用范围更广。

总结

  • 元组类型可以转为联合类型,但联合类型无法直接转为元组类型。
  • 可以用联合类型约束数组元素类型,实现基本的类型安全。
  • 进一步约束时,可以用 IsSameType 工具类型判断类型集合是否完全一致。

(❁´◡`❁) 感谢你读到这里!

相关推荐
jlspcsdn1 天前
20251222项目练习
前端·javascript·html
拉不动的猪1 天前
回顾计算属性的缓存与监听的触发返回结果
前端·javascript·vue.js
树欲静而风不止慢一点吧1 天前
小米手环9应用/游戏开发快速入门
前端·javascript·小程序
用户313050086271 天前
JavaScript中的迭代器和生成器
javascript
Crystal3281 天前
图片懒加载
前端·javascript·代码规范
Revol_C1 天前
开箱即用!轻量级轮询方案,支持同步获取轮询结果!
前端·javascript·设计模式
俊劫1 天前
AI 编码技巧篇(内部分享)
前端·javascript·ai编程
多看书少吃饭1 天前
从 ScriptProcessor 到 AudioWorklet:Electron 桌面端录音实践总结
前端·javascript·electron
钱多多8101 天前
Vue版本降级操作指南(解决依赖冲突与版本不一致问题)
前端·javascript·vue.js·前端框架