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. 对比类型ArrVersion和InputVersion是否相同
typescript 复制代码
type What = IsSameType<ArrVersion, InputVersion>;
  1. 给一个变量显式声明此类型并赋值
typescript 复制代码
const what: What = 1; // 如果ArrVersion和InputVersion不一致,这行代码会因类型不匹配而报错。
// 严谨版本这里可以赋值为true,具体什么值都没有关系,选择自己喜欢的就好

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

4. 实现 IsSameType 工具类型

直观版本

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

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

Note: 如果用true和false,那么此泛型工具会永远返回boolean类型从而失去判断能力。
Note: 不使用1和0是因为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 工具类型判断类型集合是否完全一致。

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

相关推荐
panxianren3 小时前
Bun 1.4 的 bun test --parallel 将 4 秒的测试套件缩短到约 1 秒
java·javascript
可乐鸡翅yeah_6 小时前
FFmpeg 生成 HLS 独立分片 independent‑segments 参数到底有什么用
javascript·ffmpeg·音视频·safari·m3u8
福兮说7 小时前
canvas 旋转图片的六个坑:四角被裁、Math.ceil 多出 1px 黑边、JPG 角发黑、翻转方向反了
前端·javascript·图像处理·canvas
Ai-_Man8 小时前
您您这可以把Grok的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。AI导出鸭
javascript·人工智能·ai·小程序·电脑
liangshanbo12159 小时前
JavaScript 异步核心主线
javascript·事件循环·异步操作
传人once10 小时前
悬停旋转放大和位移效果如何写
javascript·css·动画
默_笙11 小时前
🍔 中间件不只是打日志:四个钩子、一次短路,和自带的工具
前端·javascript
Dovis(誓平步青云)12 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
xieter12 小时前
【技术精选】TypeScript 高级类型体操:条件类型与模板字面量深度剖析 (2026-10-06)
javascript
ss27313 小时前
AI全栈实战 | 3.3-02 Python 并发:有 GIL 为什么还用多线程,asyncio 和 JS 事件循环同源不同味
开发语言·javascript·python