如何在 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
How to declare a Fixed length Array in TypeScript
提问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 noImplicitAny
typescript configuration directiveto be enabled in order to work (commonly recommended practice)
(*) 此解决方案需要启用noImplicitAny
typescript配置指令才能工作(通常推荐的做法)
The Array(ish) approach :
Array(ish) 方法:
This solution behaves as an augmentation of the Array
type, 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'];