typescript - 声明任意嵌套数组(递归类型定义)

标签 typescript tsc typescript3.0

假设我有这样一个函数:

const nested = function(v: string | Array<string> | Array<Array<string>>){...}

问题是 v 可能嵌套了 5 或 6 层深。如何声明任意嵌套的类型?

例如,我该如何处理:

nested([['zam'],[[[['zimm']]]]])

最佳答案

您可以相当轻松地描述任意嵌套的数组类型,如下所示:

interface NestedArray<T> extends Array<T | NestedArray<T>> { }

该类型的递归引用是允许的(其中 type 别名不允许)因为接口(interface)基类型的计算是 deferred .

不幸的是,使用它并不简单。您可以创建这种类型的值:

// works as expected
const nums: NestedArray<number> = [1,[2,[3,[4,[5],6,[7]],[8]],[[9]]]];

// errors as expected
const oops: NestedArray<number> = [1,[2,["3",[4,[5],6,[7]],[8]],[[9]]]]; // error

但是编译器会在大约五层深度之后放弃检查此类嵌套类型:

// no error!  
const what: NestedArray<number> = [[[[["a"]]]]]; // 😮

另外,您不能轻易地推断出给定数组类型的最终元素类型。例如:

declare function doesntWork<T>(arr: NestedArray<T>): T;
const t = doesntWork([[1,2,3]]) ; // T is number[] | ConcatArray<number[]>; 🙁

您可能期望 T被推断为number , 但编译器没有义务这样做,因为 [[1,2,3]]既是 NestedArray<number[]>和一个 NestedArray<number> .即使你试图强制 NestedArray<T>只接受 T这不是数组,编译器不会按照您想要的方式推断元素类型。

如果您需要推断嵌套元素类型,您会发现自己想要创建一个递归的 type别名,可能涉及 conditional types .但是你不能在 TypeScript 中这样做(无论如何从 3.2 开始)。

type NestedElementType<T> = T extends Array<infer A> ? NestedElementType<A> : T; // error 🙁

你能做的最好的事情就是选择一个支持的深度(比如 10 个级别),然后展开递归类型别名:

type NestedElementType<T> = T extends Array<infer A> ? NET1<A> : T;
type NET1<T> = T extends Array<infer A> ? NET2<A> : T;
type NET2<T> = T extends Array<infer A> ? NET3<A> : T;
type NET3<T> = T extends Array<infer A> ? NET4<A> : T;
type NET4<T> = T extends Array<infer A> ? NET5<A> : T;
type NET5<T> = T extends Array<infer A> ? NET6<A> : T;
type NET6<T> = T extends Array<infer A> ? NET7<A> : T;
type NET7<T> = T extends Array<infer A> ? NET8<A> : T;
type NET8<T> = T extends Array<infer A> ? NET9<A> : T;
type NET9<T> = T extends Array<infer A> ? NETX<A> : T;
type NETX<T> = T extends Array<infer A> ? unknown : T; // bail out

这会起作用:

declare function doesWork<N extends NestedArray<any>>(arr: N): NestedElementType<N>;
const w = doesWork([[1,2,[3],[[4]]]]) ; // returns number 🙂

考虑到所有这些警告,您可以使用这种类型:

function flatten<N extends NestedArray<any>>(arr: N): Array<NestedElementType<N>> {
  const ret: Array<NestedElementType<N>> = [];
  arr.forEach(l => {
    if (Array.isArray(l)) {
      ret.push(...flatten(l));
    } else {
      ret.push(l);
    }
  });
  return ret;
}

const flattened = flatten([["a"], ["b"], [[[[["c"]]], "d"]]]); // string[]

是否值得,就看你自己了。希望有所帮助;祝你好运!

关于typescript - 声明任意嵌套数组(递归类型定义),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53646270/

相关文章:

javascript - JSON 到达对象的列表元素

angular - 在 ng2-chart 中设置条形图的颜色

javascript - 通用无状态组件 React 的类型?或者在 typescript 中扩展通用函数接口(interface)以获得更通用的功能?

javascript - 在 Typescript 中扩展内置类型

javascript - 为什么 Typescript 接口(interface)不在 Javascript 中呈现

node.js - 使用 TypeScript 正确扩展stream.Transform类

javascript - 如何让 Typescript 编译器将编译后的 js 输出到不同的目录?

typescript - TypeScript 的条件类型

javascript - 无需编译的 TypeScript 输出文件