Объявить произвольно вложенный массив (определение рекурсивного типа) - PullRequest
0 голосов
/ 06 декабря 2018

Скажем, у меня есть такая функция:

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

проблема в том, что v может быть вложенным на 5 или 6 уровней.Как я могу объявить тип, который является произвольно вложенным?

Например, как мне справиться:

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

1 Ответ

0 голосов
/ 10 декабря 2018

Вы можете довольно легко описать произвольно вложенный тип массива, например:

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

Этот тип рекурсивной ссылки разрешен (где псевдоним type не разрешен), потому что оценка базы интерфейсатип отложенный .

К сожалению, работать с ним не так просто.Вы можете создавать значения такого типа:

// 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, возможно с участием условных типов .Но вы не можете сделать это в 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[]

Вам решать, стоит ли оно того.Надеюсь, это поможет;удачи!

...