如何在 TypeScript 中声明一个固定长度的数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41139763/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 07:30:58  来源:igfitidea点击:

How to declare a Fixed length Array in TypeScript

typescripttypesfixed-length-array

提问by henryHyman

At the risk of demonstrating my lack of knowledge surrounding TypeScript types - I have the following question.

冒着证明我对 TypeScript 类型缺乏了解的风险 - 我有以下问题。

When you make a type declaration for an array like this...

当您为这样的数组进行类型声明时...

position: Array<number>;

...it will let you make an array with arbitrary length. However, if you want an array containing numbers with a specific length i.e. 3 for x,y,z components can you make a type with for a fixed length array, something like this?

...它可以让你制作一个任意长度的数组。但是,如果您想要一个包含特定长度数字的数组,即 x,y,z 分量为 3,您可以为固定长度数组创建一个类型,像这样吗?

position: Array<3>

Any help or clarification appreciated!

任何帮助或澄清表示赞赏!

回答by Nitzan Tomer

The javascript array has a constructor that accepts the length of the array:

javascript 数组有一个接受数组长度的构造函数:

let arr = new Array<number>(3);
console.log(arr); // [undefined × 3]

However, this is just the initial size, there's no restriction on changing that:

但是,这只是初始大小,更改它没有限制:

arr.push(5);
console.log(arr); // [undefined × 3, 5]

Typescript has tuple typeswhich let you define an array with a specific length and types:

Typescript 具有元组类型,可让您定义具有特定长度和类型的数组:

let arr: [number, number, number];

arr = [1, 2, 3]; // ok
arr = [1, 2]; // Type '[number, number]' is not assignable to type '[number, number, number]'
arr = [1, 2, "3"]; // Type '[number, number, string]' is not assignable to type '[number, number, number]'

回答by colxi

The Tuple approach :

元组方法:

This solution provides a strict FixedLengthArray(ak.a. SealedArray) type signature based in Tuples.

此解决方案提供了 基于元组的严格FixedLengthArray(又名 SealedArray)类型签名。

Syntax example :

语法示例:

// Array containing 3 strings
let foo : FixedLengthArray<[string, string, string]> 

This is the safest approach, considering it prevents accessing indexes out of the boundaries.

这是最安全的方法,因为它可以防止访问超出边界的索引

Implementation :

执行 :

type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' | 'unshift' | number
type ArrayItems<T extends Array<any>> = T extends Array<infer TItems> ? TItems : never
type FixedLengthArray<T extends any[]> =
  Pick<T, Exclude<keyof T, ArrayLengthMutationKeys>>
  & { [Symbol.iterator]: () => IterableIterator< ArrayItems<T> > }

Tests :

测试:

var myFixedLengthArray: FixedLengthArray< [string, string, string]>

// Array declaration tests
myFixedLengthArray = [ 'a', 'b', 'c' ]  // ? OK
myFixedLengthArray = [ 'a', 'b', 123 ]  // ? TYPE ERROR
myFixedLengthArray = [ 'a' ]            // ? LENGTH ERROR
myFixedLengthArray = [ 'a', 'b' ]       // ? LENGTH ERROR

// Index assignment tests 
myFixedLengthArray[1] = 'foo'           // ? OK
myFixedLengthArray[1000] = 'foo'        // ? INVALID INDEX ERROR

// Methods that mutate array length
myFixedLengthArray.push('foo')          // ? MISSING METHOD ERROR
myFixedLengthArray.pop()                // ? MISSING METHOD ERROR

// Direct length manipulation
myFixedLengthArray.length = 123         // ? READ-ONLY ERROR

// Destructuring
var [ a ] = myFixedLengthArray          // ? OK
var [ a, b ] = myFixedLengthArray       // ? OK
var [ a, b, c ] = myFixedLengthArray    // ? OK
var [ a, b, c, d ] = myFixedLengthArray // ? INVALID INDEX ERROR

(*) This solution requires the noImplicitAnytypescript configuration directiveto be enabled in order to work (commonly recommended practice)

(*) 此解决方案需要启用noImplicitAnytypescript配置指令才能工作(通常推荐的做法)



The Array(ish) approach :

Array(ish) 方法:

This solution behaves as an augmentation of the Arraytype, accepting an additional second parameter(Array length). Is not as strict and safe as the Tuple based solution.

此解决方案表现为Array类型的扩充,接受额外的第二个参数(数组长度)。不像基于元组的解决方案那么严格和安全。

Syntax example :

语法示例:

let foo: FixedLengthArray<string, 3> 

Keep in mind that this approach will not prevent you from accessing an index out of the declared boundariesand set a value on it.

请记住,此方法不会阻止您访问超出声明边界的索引并为其设置值。

Implementation :

执行 :

type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' |  'unshift'
type FixedLengthArray<T, L extends number, TObj = [T, ...Array<T>]> =
  Pick<TObj, Exclude<keyof TObj, ArrayLengthMutationKeys>>
  & {
    readonly length: L 
    [ I : number ] : T
    [Symbol.iterator]: () => IterableIterator<T>   
  }

Tests :

测试:

var myFixedLengthArray: FixedLengthArray<string,3>

// Array declaration tests
myFixedLengthArray = [ 'a', 'b', 'c' ]  // ? OK
myFixedLengthArray = [ 'a', 'b', 123 ]  // ? TYPE ERROR
myFixedLengthArray = [ 'a' ]            // ? LENGTH ERROR
myFixedLengthArray = [ 'a', 'b' ]       // ? LENGTH ERROR

// Index assignment tests 
myFixedLengthArray[1] = 'foo'           // ? OK
myFixedLengthArray[1000] = 'foo'        // ? SHOULD FAIL

// Methods that mutate array length
myFixedLengthArray.push('foo')          // ? MISSING METHOD ERROR
myFixedLengthArray.pop()                // ? MISSING METHOD ERROR

// Direct length manipulation
myFixedLengthArray.length = 123         // ? READ-ONLY ERROR

// Destructuring
var [ a ] = myFixedLengthArray          // ? OK
var [ a, b ] = myFixedLengthArray       // ? OK
var [ a, b, c ] = myFixedLengthArray    // ? OK
var [ a, b, c, d ] = myFixedLengthArray // ? SHOULD FAIL

回答by Tomasz Gawel

Actually, You can achieve this with current typescript:

实际上,您可以使用当前的打字稿来实现这一点:

type Grow<T, A extends Array<T>> = ((x: T, ...xs: A) => void) extends ((...a: infer X) => void) ? X : never;
type GrowToSize<T, A extends Array<T>, N extends number> = { 0: A, 1: GrowToSize<T, Grow<T, A>, N> }[A['length'] extends N ? 0 : 1];

export type FixedArray<T, N extends number> = GrowToSize<T, [], N>;

Examples:

例子:

// OK
const fixedArr3: FixedArray<string, 3> = ['a', 'b', 'c'];

// Error:
// Type '[string, string, string]' is not assignable to type '[string, string]'.
//   Types of property 'length' are incompatible.
//     Type '3' is not assignable to type '2'.ts(2322)
const fixedArr2: FixedArray<string, 2> = ['a', 'b', 'c'];

// Error:
// Property '3' is missing in type '[string, string, string]' but required in type 
// '[string, string, string, string]'.ts(2741)
const fixedArr4: FixedArray<string, 4> = ['a', 'b', 'c'];