假设我有以下类型
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
的东西,这样我就可以使用它进行元组索引,这将是一个很大的优势。
更新2022
我已经写了一个包来处理你的问题(以及几乎所有类型级别的算术),并且它完全避免了类型实例化过深,可能是无限的;限制。
https://www.npmjs.com/package/ts-arithmetic
下面是一些使用这个包可以做的事情的例子:
import { Add, Subtract, Multiply, Divide, Pow, Compare, Mod } from 'ts-arithmetic'
// Check the docs below for more
// Add any two numbers
type AddExample = Add<1024, 256>
// 1280
// Subtract any two numbers
type SubtractExample = Subtract<400, 1000>
// -600
// Multiply any two numbers
type MultiplyExample = Multiply<25, 50>
// 1250
// Divide any two numbers
type DivideExample = Divide<-1, 800>
// -0.00125
// Raise a number to an integer power
type PowExample = Pow<5, 7>
// 78125
// Compare any two numbers (same rules as JavaScript Array.sort())
type CompareExample = Compare<123456, 20>
// 1
// Get the JavaScript mod (i.e. remainder)
type ModExmaple = Mod<87, 7>
// 3
经过一番搜索,我找到了这篇有趣的文章——在TypeScript的类型系统中实现算术——它确实展示了一种使用一些前沿特性来实现你想要的东西的方法。
可以创建一个将两个数字相加的类型,如下所示:
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.