TypeScript中如何定义可自动推断长度的关联固定长度数组类型?
你想要定义一个foo<T>类型,要求对象的length属性与array数组的长度严格匹配——比如{length: 2, array: ["a","b"]}符合类型,{length: 3, array: ["a","b"]}不符合,同时希望不需要手动指定长度泛型,直接用let bar : foo<string> = {length: 2, array: ["a","b"]}的形式。
你的两种伪代码无法实现的原因
第一种写法:
type foo<T> = { length: number; array: T[length] }这里
length是宽泛的number类型,不是具体的数字字面量,且T[length]语法不合法——length并非T的索引类型,也无法关联数组的实际长度。第二种写法:
type foo<T> = { length: array.length; array: T[] }TypeScript的类型系统不支持在对象类型定义中直接引用同对象的其他属性值作为类型,这种写法不符合语法规则。
现有方案的局限性
基于FixedLengthArray<T,N extends number>的写法:
type foo<T,N extends number> = { length: N; array: FixedLengthArray<T,N> }
虽然能保证长度匹配,但必须手动指定N,无法自动推断,不符合你想要省略长度泛型的需求。
可行的实现方式
要让TypeScript自动推断数组长度,最优雅的方式是利用泛型推断结合辅助函数来实现:
首先定义固定长度数组的类型(已有该类型可跳过):
type FixedLengthArray<T, N extends number> = N extends N ? number extends N ? T[] : _FixedLengthArray<T, N, []> : never; type _FixedLengthArray<T, N extends number, R extends T[]> = R['length'] extends N ? R : _FixedLengthArray<T, N, [...R, T]>;
然后定义Foo类型和辅助函数,让函数自动推断长度泛型N:
type Foo<T, N extends number> = { length: N; array: FixedLengthArray<T, N>; }; // 辅助函数:通过传入的对象自动推断T和N function createFoo<T, N extends number>(obj: Foo<T, N>): Foo<T, N> { return obj; }
使用示例
// 正常通过:TS自动推断N为2 const bar = createFoo({ length: 2, array: ["a", "b"] }); // 类型报错:数组长度与length不匹配 const badBar = createFoo({ length: 3, array: ["a", "b"] });
如果一定要写成let bar: Foo<string> = ...的形式,需要结合as const断言让TypeScript识别字面量类型,从而推断具体长度:
const bar: Foo<string> = { length: 2 as const, array: ["a", "b"] as const };
总结
直接用你给出的两种伪代码写法无法实现需求,TypeScript不支持在对象类型定义中动态关联同对象的属性值类型。通过辅助函数触发泛型推断,是实现自动匹配length与数组长度的最优方案;如果坚持省略手动指定泛型,可结合as const断言让TypeScript识别字面量类型来完成推断。
内容的提问来源于stack exchange,提问作者Script Raccoon

