在TypeScript中,如何在文字的并集上实现类型级别的max函数?

在TypeScript中,如何在文字的并集上实现类型级别的max函数?,typescript,types,Typescript,Types,给定数量字面值的并集,例如: 类型值=1 | 2 | 5 是否可以创建提取最大值的泛型类型级别函数,即: 类型Max=??? V型=最大值 //V=5 哦,孩子。我创建了一个似乎有效的怪物,但由于循环类型的限制,它不能作为一个单一的步骤来完成 type DropFirst<T extends TupleLike> = T extends readonly [any, ...infer U] ? U : [...T]; type Longer<T, U extends Tupl

给定数量字面值的并集,例如:

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

类型Max=??? V型=最大值 //V=5
哦,孩子。我创建了一个似乎有效的怪物,但由于循环类型的限制,它不能作为一个单一的步骤来完成

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

我打赌有一个更简单的方法-

哦,孩子。我创建了一个似乎有效的怪物,但由于循环类型的限制,它不能作为一个单一的步骤来完成

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
我打赌有一个更简单的方法-

在TS4.0中使用slated的Max最简洁的版本是:

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
另一种方法是以编程方式生成一个助手类型,如LEQ,它由小于或等于给定键的所有非负整数组成,最大值为您可以选择的某个最大值。。。我在做100或99之类的事情:

// 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!
然后,您可以编写一些代码,在不使用非法循环的情况下提取正确的值:

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]
当您给它一个意外的值(如-1)时,它仍然不起作用,但在这里,它至少只给了您它所具有的最大值,而不是完全偏离最深处:

type OopsNegativeOkayIGuess = Max<-1 | 3> // 99
这就给了你

type OopsNegativeOkayIGuess = Max<-1 | 3> // 3
这恰好是真的,尽管Max将是1,这是假的

在任何情况下,这都会使编译器超越我推荐的任何产品代码库。实际上,我们应该推动实现,这样编译器就可以在类型级别进行数学运算。所以你可能想去那里,给它一个我能想到的最简洁的版本Max,它使用了TS4.0中的slated,如下所示:

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
另一种方法是以编程方式生成一个助手类型,如LEQ,它由小于或等于给定键的所有非负整数组成,最大值为您可以选择的某个最大值。。。我在做100或99之类的事情:

// 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!
然后,您可以编写一些代码,在不使用非法循环的情况下提取正确的值:

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]
当您给它一个意外的值(如-1)时,它仍然不起作用,但在这里,它至少只给了您它所具有的最大值,而不是完全偏离最深处:

type OopsNegativeOkayIGuess = Max<-1 | 3> // 99
这就给了你

type OopsNegativeOkayIGuess = Max<-1 | 3> // 3
这恰好是真的,尽管Max将是1,这是假的


在任何情况下,这都会使编译器超越我推荐的任何产品代码库。实际上,我们应该推动实现,这样编译器就可以在类型级别进行数学运算。所以你可能想去那里给它一个简单的答案,它必须是一个并集,还是可以是一个文本的元组?将一个文本的元组转换成一个文本的并集很容易,但相反的路径却不容易。不过,我很想看到任何可能的解决方案的提示,所以选择最适合您的。我认为typescript没有比较运算符,所以您无法知道类型5>类型1。无论如何,我看不到任何好处。当然。使用比较运算符,这将是显而易见的。然而,由于TypeScript类型系统是图灵完备的,我想知道在其类型系统中是否存在表达不等式的巧妙/黑客方法。它必须是一个并集,还是可以是一个文本元组?将一个文本元组转换为一个文本并集很容易,但相反的路径却不容易。不过,我很想看到任何可能的解决方案的提示,所以选择最适合您的。我认为typescript没有比较运算符,所以您无法知道类型5>类型1。无论如何,我看不到任何好处。当然。使用比较运算符,这将是显而易见的。然而,由于TypeScript类型系统是图灵完备的,我想知道它是否存在任何在其类型系统中表达不等式的巧妙/黑客方法。+1对于迁移到元组世界以利用长度的精彩提示。但是,您的较长函数仅在逐项递增的情况下有效。因此,像[4,5,7,1,2]这样的输入结果是5而不是7。不管怎样,你在做什么+1了解迁移到元组世界以利用长度的精彩提示。但是,您的较长函数仅在逐项递增的情况下有效。因此,像[4,5,7,1,2]这样的输入结果是5而不是7。不管怎样,你在做什么;非常感谢你的回答,ToLEQ非常整洁。为了RegularTypeScript的用户基础,我在这里链接了使用递归条件类型的风险。我还链接到显示这件事变得多么令人困惑。非常感谢你伟大的回答I.a.,ToLEQ是如此的整洁。为了RegularTypeScript的用户基础,我在这里链接了使用递归条件类型的风险。我还链接到显示这件事变得多么混乱。