【TS】如何在 typescript 中迭代 union 类型?

6 篇文章 0 订阅

如何在 ts 中单独操作 union 中的每个元素?

如果我们的 union is assignable to PropertyKey,那么将会非常简单,我们可以在 mapping type 中单独操作他们:

type iterate<U extends PropertyKey> = {
  [key in U]: key // mapping to anything you want
}

但若 union 中存在额外情况,那就需要使用其他办法了。

将 union 拆解开来并单独访问,我们知道 conditional type 有这个能力,因为 union 在其中会被拆解并分别 distribute 到单独的表达式中,但可惜的是,在运算结束后,这些值最终仍然会被 union 到一起,这个行为我们无法控制。

此外,依托函数重载的某些特性,也可以对 union 的元素进行单独访问,这种方法有点 hack,先看这个例子:

declare function fun(): 'a'
declare function fun(): 'b'

type test = ReturnType<typeof fun> // 'b'

对重载的函数,推断其函数签名时总以最后一个为准。

When inferring from a type with multiple call signatures (such as the type of an overloaded function), inferences are made from the last signature (which, presumably, is the most permissive catch-all case). It is not possible to perform overload resolution based on a list of argument types.

typescript2.8 release notes

而函数类型的交集在该行为上与函数重载一致,即:

type fun = (() => 'a') & (() => 'b')

type test = ReturnType<fun> // 'b'

既然如此,我们可以将 union type 的每一个元素映射到"带有该元素类型返回值(或参数也可以)的函数类型",然后取个交集即可。

type ToFun<U> = U extends any ? () => U : never; // (() => 'a') | (() => 'b')

然后取交集:

type U2I<U> = (U extends any ? (u: U) => any : never) extends (i: infer I) => any ? I : never
type Intersection<U> = U2I<ToFun<U>> // (() => 'a') & (() => 'b')

然后我们来开始尝试操作每个元素:

type LastOfUnion<U> = Intersection<U> extends () => infer R ? R /* the last of the overload */: never

type iterate<U> = LastOfUnion<U> extends never ? never : /* do sth */ iterate<Exclude<U, LastOfUnion<U>>>

通过将当前的 last 元素 exclude 出去,然后进入下一层递归,我们就可以在一个 union 上迭代了。

一个例子是 type-challengs/union-to-tuple,尝试一下!

ref

typescript2.8 release notes

type-challengs/union-to-tuple

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高厉害

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值