简体   繁体   English

Typescript中具有最小和最大长度的数组的类型定义

[英]Type definition for array with minimun and maximum length in Typescript

How can I create a type definition for array with minimun and maximum length, as in:如何为具有最小和最大长度的数组创建类型定义,如下所示:

var my_arr:my_type<type, {start: 1, end: 3}>;

my_arr = [1] - pass
my_arr = [1,2] - pass
my_arr = [1,2,3] - pass
my_arr = [1,2,3,4] - fail
my_arr = [1, "string"] - fail

IE provide the type of the elements in the array and a start index and end index of length such that if the length is less than the start index it fails, or if the length is bigger than the end index it fails. IE 提供数组中元素的类型以及长度的起始索引和结束索引,这样如果长度小于起始索引则失败,或者如果长度大于结束索引则失败。

I've found this solution for a fixed length array online:我在网上找到了一个固定长度数组的解决方案:

type SingleLengthArray<
  T,
  N extends number,
  R extends readonly T[] = []
> = R["length"] extends N ? R : SingleLengthArray<T, N, readonly [T, ...R]>;

And I was able to create a type that works if the length of the array is either equal to the start index or end index but nothing inbetween:如果数组的长度等于开始索引或结束索引但两者之间没有任何内容,我能够创建一个有效的类型:

type MultipleLengthArray<
  T,
  N extends { start: number; end: number },
  R extends readonly T[] = []
> = SingleLengthArray<T, N["start"], R> | SingleLengthArray<T, N["end"], R>;

What I think would work if it's possible is to create an array of numbers from the start index up to the end index(ie start index = 1, end index = 5 => array would be [1,2,3,4,5]) and iterate over the array and like如果可能的话,我认为会起作用的是创建一个从开始索引到结束索引的数字数组(即开始索引 = 1,结束索引 = 5 => 数组将是 [1,2,3,4,5 ]) 并遍历数组等

type MultipleLengthArray<T, N extends {start: number, end:number}, R extends readonly T[] = []> = for each value n in array => SingleLengthArray<T, n1, R> | SingleLengthArray<T, n2, R> | ... | SingleLengthArray<T, n_last, R>

Please let me know if there is a way to do this, thank you.请告诉我是否有办法做到这一点,谢谢。

I interpret this as looking for a type function we can call TupMinMax<T, Min, Max> which resolves to a tuple type where each element is of type T , and whose length must be between Min and Max inclusive.我将此解释为寻找一个我们可以调用TupMinMax<T, Min, Max>的类型函数,它解析为一个元组类型,其中每个元素的类型都是T ,并且其长度必须介于MinMax之间。 We can represent this as a single tuple type with optional elements at every index greater than Min and less than or equal to Max .我们可以将其表示为单个元组类型,每个索引处的可选元素都大于Min且小于或等于Max (Unless you turn on the --exactOptionalPropertyTypes compiler option , this will allow undefined values for these optional properties also, but I'm going to assume that's not a big deal). (除非您打开--exactOptionalPropertyTypes编译器选项,否则这也将允许这些可选属性的undefined值,但我假设这没什么大不了的)。 So you want, for example, TupMinMax<number, 1, 3> to be [number, number?, number?] .因此,例如,您希望TupMinMax<number, 1, 3>[number, number?, number?]

Here's one approach:这是一种方法:

type TupMinMax<
  T, Min extends number, Max extends number,
  A extends (T | undefined)[] = [], O extends boolean = false
  > = O extends false ? (
    Min extends A['length'] ? TupMinMax<T, Min, Max, A, true> : 
    TupMinMax<T, Min, Max, [...A, T], false>
  ) : Max extends A['length'] ? A : 
    TupMinMax<T, Min, Max, [...A, T?], false>;

This is a tail-recursive conditional type , where TupMinMax<T, Min, Max> has some extra parameters A and O to act as accumulators to store intermediate state.这是一个尾递归条件类型,其中TupMinMax<T, Min, Max>有一些额外的参数AO作为累加器来存储中间状态。 Specifically, A will store the tuple result so far, while O will be either true or false representing whether we have entered into the optional part of the tuple.具体来说, A将存储到目前为止的元组结果,而O将是truefalse ,表示我们是否已进入元组的可选部分。 It starts out false and becomes true later.它开始是false的,后来变成true

The first conditional check is O extends false ? (...) : (...)第一个条件检查是O extends false ? (...) : (...) O extends false ? (...) : (...) . O extends false ? (...) : (...) If O is false then we haven't yet reached the minimum length and the elements should be required.如果Ofalse ,那么我们还没有达到最小长度,应该需要元素。 Then we check Min extends A['length'] to see if the accumulator has reached the minimum length yet.然后我们检查Min extends A['length']以查看累加器是否已达到最小长度。 If so, then we immediately switch O to true with the same A accumulator.如果是这样,那么我们立即使用相同A累加器将O切换为true If not, then we append a required T element to the end of A .如果不是,那么我们将所需的T元素附加到A的末尾。 If O is not false then it's true and we then check Max extends A['length'] to see if the accumulator has reached the maximum length yet.如果O不为false ,则为true ,然后我们检查Max extends A['length']以查看累加器是否已达到最大长度。 If so then we are done and evaluate to A .如果是这样,那么我们完成并评估为A If not, then we append an optional T element to the end of A .如果不是,那么我们将一个可选的T元素附加到A的末尾。

Let's test it out:让我们测试一下:

type OneTwoOrThreeNumbers = TupMinMax<number, 1, 3>;
// type OneTwoOrThreeNumbers = [number, number?, number?]

let nums: OneTwoOrThreeNumbers;
nums = []; // error
nums = [1]; // okay
nums = [1, 2]; // okay
nums = [1, 2, 3]; // okay
nums = [1, 2, 3, 4]; // error
nums = [1, "two", 3]; // error

type BetweenTenAndThirtyStrings = TupMinMax<string, 10, 30>;
/* type BetweenTenAndThirtyStrings = [string, string, string, string, 
     string, string, string, string, string, string, string?, string?, 
     string?,  string?, ... 15 more ...?, string?] */
let strs: BetweenTenAndThirtyStrings;
strs = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"]

Looks good.看起来不错。 The tail recursion means that the compiler should be able to handle a recursion depth of ~1,000 levels, so if your tuple length range is even as large as several hundred it should compile okay.尾递归意味着编译器应该能够处理大约 1,000 级的递归深度,所以如果你的元组长度范围甚至大到几百,它应该可以编译。

Note that such recursive types can be fragile and prone to nasty edge cases.请注意,此类递归类型可能很脆弱并且容易出现令人讨厌的边缘情况。 If you like to torture yourself and your compiler you try passing something other than non-negative whole numbers as Min and Max , or pass in a Min which is greater than Max .如果你想折磨自己和你的编译器,你可以尝试传递非负整数以外的东西作为MinMax ,或者传递一个大于MaxMin The recursion base case will never be reached and the compiler will, if you're lucky, complain about recursion depth;递归基本情况永远不会达到,如果幸运的话,编译器会抱怨递归深度; and if you're not lucky, it will consume lots of CPU and make your computer hot:如果你不走运,它会消耗大量 CPU 并使你的计算机变热:

// 🔥💻🔥 followed by 
// Type instantiation is excessively deep and possibly infinite.
// type Oops = TupMinMax<any, 10, 1>; 
// type Oops2 = TupMinMax<any, 3.5 4>;

So be careful.所以要小心。

Playground link to code Playground 代码链接

You may be able to leverage a Tuple with Optional Properties.您可以利用具有可选属性的元组。 Tuples have a specified length (allowing for the optionality of its properties).元组具有指定的长度(允许其属性的可选性)。

enum E { V, A, S }

type TupleOfE = [ E, E, E, E?, E? ]

const e: TupleOfE = [ E.V, E.A, E.S, E.S, E.S ]

console.log(e)
console.log(e.filter(e => e === E.S))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM