冒着显示我对TypeScript类型缺乏了解的风险,我有以下问题。
当你为这样的数组做类型声明时…
position: Array<number>;
...它可以让你创建任意长度的数组。然而,如果你想要一个包含特定长度的数字的数组,例如x y z组件的3,你可以为一个固定长度的数组创建一个类型,像这样吗?
position: Array<3>
任何帮助或澄清感谢!
冒着显示我对TypeScript类型缺乏了解的风险,我有以下问题。
当你为这样的数组做类型声明时…
position: Array<number>;
...它可以让你创建任意长度的数组。然而,如果你想要一个包含特定长度的数字的数组,例如x y z组件的3,你可以为一个固定长度的数组创建一个类型,像这样吗?
position: Array<3>
任何帮助或澄清感谢!
当前回答
对于任何需要比@ThomasVo的正确处理非文字数字的更通用的解决方案的人:
type LengthArray<
T,
N extends number,
R extends T[] = []
> = number extends N
? T[]
: R['length'] extends N
? R
: LengthArray<T, N, [T, ...R]>;
我需要使用这种类型来正确地处理未知长度的数组。
type FixedLength = LengthArray<string, 3>; // [string, string, string]
type UnknownLength = LengthArray<string, number>; // string[] (instead of [])
其他回答
这有点晚了,但如果您使用只读数组([]作为const),这里有一种方法
interface FixedLengthArray<L extends number, T> extends ArrayLike<T> {
length: L
}
export const a: FixedLengthArray<2, string> = ['we', '432'] as const
在const值中添加或删除字符串将导致此错误-
Type 'readonly ["we", "432", "fd"]' is not assignable to type 'FixedLengthArray<2, string>'.
Types of property 'length' are incompatible.
Type '3' is not assignable to type '2'.ts(2322)
OR
Type 'readonly ["we"]' is not assignable to type 'FixedLengthArray<2, string>'.
Types of property 'length' are incompatible.
Type '1' is not assignable to type '2'.ts(2322)
分别。
编辑(05/13/2022):相关的未来TS功能-满足这里的定义
对于任何需要比@ThomasVo的正确处理非文字数字的更通用的解决方案的人:
type LengthArray<
T,
N extends number,
R extends T[] = []
> = number extends N
? T[]
: R['length'] extends N
? R
: LengthArray<T, N, [T, ...R]>;
我需要使用这种类型来正确地处理未知长度的数组。
type FixedLength = LengthArray<string, 3>; // [string, string, string]
type UnknownLength = LengthArray<string, number>; // string[] (instead of [])
JavaScript数组有一个接受数组长度的构造函数:
let arr = new Array<number>(3);
console.log(arr); // [undefined × 3]
然而,这只是初始大小,没有任何限制可以更改:
arr.push(5);
console.log(arr); // [undefined × 3, 5]
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]'
typescript v4.6,下面是一个基于Tomasz Gawel回答的超短版本
type Tuple<
T,
N extends number,
R extends readonly T[] = [],
> = R['length'] extends N ? R : Tuple<T, N, readonly [T, ...R]>;
// usage
const x: Tuple<number,3> = [1,2,3];
x; // resolves as [number, number, number]
x[0]; // resolves as number
还有其他方法施加length属性的值,但不是很漂亮
// TLDR, don't do this
type Tuple<T, N> = { length: N } & readonly T[];
const x : Tuple<number,3> = [1,2,3]
x; // resolves as { length: 3 } | number[], which is kinda messy
x[0]; // resolves as number | undefined, which is incorrect
元组方法:
该解决方案提供了一个严格的FixedLengthArray (aka .a。基于元组的SealedArray类型签名。
语法示例:
// Array containing 3 strings
let foo : FixedLengthArray<[string, string, string]>
这是最安全的方法,因为它可以防止在边界之外访问索引。
实现:
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> > }
测试:
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
(*)此解决方案需要启用noImplicitAny typescript配置指令才能工作(通常推荐的做法)
数组(ish)方法:
此解决方案充当Array类型的扩充,接受额外的第二个参数(Array length)。不像基于元组的解决方案那样严格和安全。
语法示例:
let foo: FixedLengthArray<string, 3>
请记住,这种方法不会阻止您在声明的边界之外访问索引并在其上设置值。
实现:
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>
}
测试:
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