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]'
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>;
示例:
// 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'];
编辑(很长时间后)**
这应该可以处理更大的大小(因为基本上它会按指数级增长数组,直到最接近2的幂):
type Shift<A extends Array<any>> = ((...args: A) => void) extends ((...args: [A[0], ...infer R]) => void) ? R : never;
type GrowExpRev<A extends Array<any>, N extends number, P extends Array<Array<any>>> = A['length'] extends N ? A : {
0: GrowExpRev<[...A, ...P[0]], N, P>,
1: GrowExpRev<A, N, Shift<P>>
}[[...A, ...P[0]][N] extends undefined ? 0 : 1];
type GrowExp<A extends Array<any>, N extends number, P extends Array<Array<any>>> = A['length'] extends N ? A : {
0: GrowExp<[...A, ...A], N, [A, ...P]>,
1: GrowExpRev<A, N, P>
}[[...A, ...A][N] extends undefined ? 0 : 1];
export type FixedSizeArray<T, N extends number> = N extends 0 ? [] : N extends 1 ? [T] : GrowExp<[T, T], N, [[T]]>;
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)
或
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)
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
6条答案
按热度按时间6pp0gazn1#
JavaScript数组具有接受数组长度的构造函数:
然而,这只是初始大小,没有限制更改:
TypeScript具有元组类型,允许您定义具有特定长度和类型的数组:
z9gpfhce2#
元组方法:
此解决方案提供了基于元组的严格FixedLengthArray(又名SealedArray)类型签名。
这是最安全的方法,考虑到它防止访问边界之外的索引。
(*)此解决方案需要启用
noImplicitAny
类型脚本configuration directive才能工作(通常推荐的做法)阵列(ish)方法:
此解决方案的行为相当于
Array
类型的扩展,接受额外的第二个参数(数组长度)。不如 * 基于元组的解决方案 * 严格和安全。请记住,这种方法不会阻止您访问声明边界之外的索引并在其上设置值。
py49o6xq3#
实际上,你可以用当前的 typescript 实现这一点:
示例:
这应该可以处理更大的大小(因为基本上它会按指数级增长数组,直到最接近2的幂):
j8yoct9x4#
现在说这个有点晚了,但如果您使用的是只读数组(
[] as const
),这里有一种方法-在
const a
值中添加或删除字符串会导致此错误-或
分别。
编辑(2022年5月13日):相关的未来TS功能-
satisfies
defined heref5emj3cl5#
下面是一个基于TomaszGawel的answer的超短版本,其中包含了typescript
v4.6
还有其他一些方法可以强制使用length属性的值,但效果并不好
lstz6jyr6#
对于任何需要比@ThomasVo中正确处理非字面数字的解决方案更通用的解决方案的人:
我需要使用这个类型来正确地处理未知长度的数组。