有没有办法在打字稿的类型级别上将两个数字相加?

Posted

技术标签:

【中文标题】有没有办法在打字稿的类型级别上将两个数字相加?【英文标题】:Is there a way to add two numbers together on the type level in typescript? 【发布时间】:2021-10-04 14:36:08 【问题描述】:

假设我有以下类型

type One = 1;
type SmallEvens = 0 | 2 | 4 | 6 | 8 | 10;
type Three = 3;

有没有我可以定义的某种类型Add<T, U> 像这样在类型级别添加数字?

type SmallOdds = Add<One, SmallEvens>; // same as `1 | 3 | 5 | 7 | 9 | 11`
type Four = Add<One, Three> // same as `4`

或者,我应该寻找数字的替代表示来实现这种效果吗?但是,能够转换为 extends number 以便我可以将其用于元组索引将是一大优势。

【问题讨论】:

【参考方案1】:

经过一些简单的搜索后,我发现了这篇引人入胜的文章 - Implementing Arithmetic Within TypeScript’s Type System - 它确实展示了一种使用一些尖端功能实现您尝试的方法。

我们可以创建一个将两个数字相加的类型,如下所示:

type Length<T extends any[]> = 
    T extends  length: infer L  ? L : never;

type BuildTuple<L extends number, T extends any[] = []> = 
    T extends  length: L  ? T : BuildTuple<L, [...T, any]>;

type Add<A extends number, B extends number> = 
    Length<[...BuildTuple<A>, ...BuildTuple<B>]>;

type Seven = Add<3, 4> // 7

我建议阅读这篇文章以了解作者的解释(以及更多算术类型)。但是,简而言之:

Length 类型推断传递给它的数组类型的长度属性。如果该数组类型是元组,它将提供实际长度,而不仅仅是number

Length<[number, number, string]> // 3

BuildTuple 类型将创建一个长度为 L 的元组类型,填充类型为 any。这通过使用扩展运算符递归地增加元组的长度,直到它与length: L 的条件匹配,此时它将被返回。

BuildTuple<3> // [any, any, any]

Add 类型现在应该是不言自明的了。我们根据所提供的两个数字的两个长度构建元组,然后将它们传播到一个新的元组并获得组合长度。

虽然作者不包括添加到联合类型上,但我们可以使用映射类型自己轻松地做到这一点:

type MappedAdd<A extends number, B extends number> = 
  [key in B]: Add<A, key>
[B]

type One = 1;
type SmallEvens = 0 | 2 | 4 | 6 | 8 | 10;

type SmallOdds = MappedAdd<One, SmallEvens>; // 1 | 3 | 5 | 7 | 9 | 11

对于这个用例,它工作得很好。但是,最终元组创建类型的递归会变得太深,将其限制为 45 以下的数字(或映射版本的 44):

Add<1, 45> // Type instantiation is excessively deep and possibly infinite.

【讨论】:

以上是关于有没有办法在打字稿的类型级别上将两个数字相加?的主要内容,如果未能解决你的问题,请参考以下文章

带有打字稿的角度材料设计

为基于打字稿的库在 package.json 类型字段中放置啥

如何在打字稿的泛型类中创建类型为“T”的新对象?

带有打字稿的Vue2,类型上不存在属性

LeetCode.2-两个数字相加(Add Two Numbers)

使用带有打字稿的 Vuex 4,类型“ComponentPublicInstance”上不存在属性“$store”