首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在TypeScript中的文本联合上实现类型级max函数?

如何在TypeScript中的文本联合上实现类型级max函数?
EN

Stack Overflow用户
提问于 2020-07-18 13:08:30
回答 2查看 252关注 0票数 2

给定数字文字的联合,例如:

代码语言:javascript
复制
type Values = 1 | 2 | 5

是否可以创建提取最大值的泛型类型级别的函数,即:

代码语言:javascript
复制
type Max<T> = ???

type V = Max<Values>
//   V = 5
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-07-19 03:51:14

我可以想出的最非法(参见微软/打字稿#26980)版本的Max (使用TS4.0中的多元元组 )是这样的:

代码语言:javascript
复制
type MaxIllegal<N extends number, T extends any[] = []> = {
    b: T['length'], r: MaxIllegal<N, [0, ...T]>
}[[N] extends [Partial<T>['length']] ? "b" : "r"];

type MaxValuesIllegal = MaxIllegal<Values>; // 5

这种非法循环直到它不起作用,这也是它不被支持的原因之一。如果输入一个非负整数以外的值,则它会递归,直到出现编译器错误:

代码语言:javascript
复制
type OopsNegative = MaxIllegal<-1 | 3>; // error! Type instantiation is excessively deep and possibly infinite.

这可能是可以解决的,但它会变得更糟:这里的递归只在几十个级别之后就会触底,所以您甚至不能在没有错误的情况下使用像55这样的数字:

代码语言:javascript
复制
type OopsTooBig = MaxIllegal<3 | 55 | 9>; // error! Type instantiation is excessively deep and possibly infinite.

另一种选择是以编程方式生成一个助手类型,如LEQ,它由所有小于或等于给定键的非负整数组成,最多可以选择一个最大值.我在做100或99之类的事情:

代码语言:javascript
复制
// console.log("type LEQ={"+Array.from({length:100},(_,i)=>i+":"+Array.from({length: i+1},(_,j)=>j).join("|")).join(", ")+"}")
type LEQ = { 0: 0, 1: 0 | 1, 2: 0 | 1 | 2, 3: 0 | 1 | 2 | 3, 4: 0 | 1 | 2 | 3 | 4, 5: 0 | 1 | 2 | 3 | 4 | 5, 6: 0 | 1 | 2 | 3 | 4 | 5 | 6, 7: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7, 8: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8, 9: 0 | 1 | 2 | 3 | 4 | 5 ... SNIP!

然后,可以编写一些代码,在不使用非法循环的情况下提取出正确的值:

代码语言:javascript
复制
type Same<T, U, Y = T, N = never> = [T] extends [U] ? [U] extends [T] ? Y : N : N;
type ToLEQ<N extends number> = { [K in keyof LEQ]: [N] extends [Exclude<LEQ[K], K>] ? never : K }[keyof LEQ]
type Max<N extends number, U extends number = ToLEQ<N>> = { [K in keyof LEQ]: Same<U, LEQ[K], K> }[keyof LEQ]

它适用于您的示例:

代码语言:javascript
复制
type MaxValues = Max<Values>; // 5

这现在适用于55,因为我们没有重载堆栈:

代码语言:javascript
复制
type NotTooBigAnymore = Max<3 | 55 | 9> // 55

当您给它一个像-1这样的意想不到的值时,它仍然不起作用,但在这里,它至少只是给出了它所拥有的最大值,而不是完全脱离了深度:

代码语言:javascript
复制
type OopsNegativeOkayIGuess = Max<-1 | 3> // 99

正如我说过的,在运行Max之前,您可能可以通过过滤传入值来绕过它。毕竟,我们有一个大的LEQ类型坐在周围:

代码语言:javascript
复制
type Max<N extends number, U extends number = ToLEQ<Extract<N, keyof LEQ>>> = { [K in keyof LEQ]: Same<U, LEQ[K], K> }[keyof LEQ]

这给了你

代码语言:javascript
复制
type OopsNegativeOkayIGuess = Max<-1 | 3> // 3

这恰好是真(尽管Max<1 | 2.5>将是1,它是假的)。

在任何情况下,这实际上都在推动编译器超越我为生产代码库推荐的任何东西。实际上,我们应该推动实现微软/打字稿#26382,这样编译器就可以在类型级别上进行计算。因此,您可能想去那里给它一个,并可能描述您的(希望是令人信服的)用例。

好吧,希望这能帮上忙,祝你好运!

操场链接到代码

票数 4
EN

Stack Overflow用户

发布于 2020-07-18 16:59:16

哦,天哪。我创建了一个看起来很有效的怪物,但是由于循环类型的限制,它不能作为一个步骤来完成。

代码语言:javascript
复制
type DropFirst<T extends TupleLike> = T extends readonly [any, ...infer U] ? U : [...T];

type Longer<T, U extends TupleLike> = U extends [] ? T : T extends [any, ...U] ? T : U;

type TupleLike<T = unknown> = readonly T[] | readonly [T];

type Head<T> = T extends [infer U, ...any[]] ? U : never;

type TupleOf<T, Length extends number, Queue extends any[] = []> = {
  done: Queue,
  next: TupleOf<T, Length, Prepend<T, Queue>>;
}[Yield<Queue, Length>]

type Yield<Queue extends any[], Length extends number> =
  Queue['length'] extends Length
    ? "done"
    : "next"

type Prepend<T, U extends any[]> =
  ((head: T, ...tail: U) => void) extends ((...all: infer V) => void)
    ? V
    : []

type FindLongestTuple<T extends readonly number[][], Accumulator extends number[] = []> = {
  done: Accumulator;
  next: FindLongestTuple<DropFirst<T>, Longer<Head<T>, Accumulator>>;
}[T extends [] ? 'done' : 'next'];

type GenerateTuples<T extends readonly number[]> = {
  [K in keyof T]: TupleOf<1, T[K] & number>
}

/**
 * Usage:
 */
type Input = [4, 5, 6, 1, 2];

type Step1 = GenerateTuples<Input>;
type Step2 = FindLongestTuple<Step1>
type Result = Step2['length']; // 6

游乐场

我敢打赌有更简单的方法可以做到这一点。;-)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62968955

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档