这个问题与其说是实际的,不如说是学术的.

我有一个函数filledArray(val, ...dims),它接受填充值和可变维数,并构建一个填充了该值的嵌套多维数组,以满足维度中的规范.例如:

> filledArray('x', 5)
[ 'x', 'x', 'x', 'x', 'x' ]

> filledArray('x', 2, 5)
[ [ 'x', 'x', 'x', 'x', 'x' ],
  [ 'x', 'x', 'x', 'x', 'x' ], ]

> filledArray('x', 2, 5, 1)
[ [ [ 'x' ], [ 'x' ], [ 'x' ], [ 'x' ], [ 'x' ] ],
  [ [ 'x' ], [ 'x' ], [ 'x' ], [ 'x' ], [ 'x' ] ] ]

TypeScrip推断该函数的返回类型为any.是否可以在类型系统中以某种方式表示真正的返回类型,从而将上面的三个返回值正确地类型化为string[]string[][]string[][][]?(对于越来越深的数组,依此类推?)

此函数的一个示例实现:

function filledArray<T>(val: T, ...dims: number[]) {
    if (dims.length == 0)
        return val

    let [dim, ...rest] = dims
    return Array.from(Array(dim), _ => filledArray(val, ...rest))
}

有趣的是,指定固定数量的重载声明似乎会使函数中的最后...rest个不知何故是错误的类型...

推荐答案

这样的返回类型可以用递归条件类型表示.

type FilledArray<N extends number[], T> = 
  N extends [number, ...infer Rest extends number[]]
    ? FilledArray<Rest, T>[]
    : T

function filledArray<T, N extends number[]>(val: T, ...dims: N): FilledArray<N, T> {
  if (dims.length == 0)
    return val as unknown as FilledArray<N, T>

  let [dim, ...rest] = dims
  return Array.from(Array(dim), _ => filledArray(val, ...rest)) as FilledArray<N, T>
}

我们可以递归地判断dims个元组包含多少个数字,并 for each 数字的返回类型添加[].

第一条件N extends [number, ...infer Rest extends number[]]判断元组N中是否仍有至少一个元素,并且同时存储推断类型Rest中除了第一个元素之外的每个元素.如果没有更多的元素,Rest也可能是空的元组.如果条件为真,我们用Rest递归调用该类型,并在其末尾加上[].如果条件为假,我们可以返回类型T以结束递归.

由于TypeScrip不理解条件返回类型如何与函数实现相关,因此我们必须使用type assertions作为返回值.

以下是一些测试:

const a = filledArray('x', 5)
// const a: "x"[]

const b = filledArray('x', 2, 5)
// const b: "x"[][]

const c = filledArray(0, 2, 5, 1)
// const c: 0[][][]

Playground

Typescript相关问答推荐

打印脚本丢失函数的泛型上下文

通过字符串索引访问对象';S的值时出现文字脚本错误

等待用户在Angular 函数中输入

限制返回联合的TS函数的返回类型

RXJS将对键盘/鼠标点击组合做出react

在Cypress中,是否可以在不标识错误的情况下对元素调用.Click()方法?

错误TS7030:并非所有代码路径都返回值.";由tsfig.json中的";Strong";:False引起

字符串文字联合的分布式条件类型

接口中可选嵌套属性的类型判断

编剧- Select 动态下拉选项

Karma Angular Unit测试如何创建未解析的promise并在单个测试中解决它,以判断当时的代码是否只在解决后运行

断言同级为true或抛出错误的Typescript函数

在类型{}上找不到带有string类型参数的索引签名 - TypeScript

如何从 Select 元素中删除选项

设置 --noImplicitAny 时,Typescript 编译器未标记返回 Any 的函数

变量可以是类型保护吗

TypeScript 5 - 从属性decorator 访问类构造函数

Object.values() 不从联合或记录派生类型

如何重新映射函数参数的对象类型以生成所需的类型形状

无法从其他子接口键名扩展接口