Вы можете довольно легко описать произвольно вложенный тип массива, например:
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[]
Вам решать, стоит ли оно того.Надеюсь, это поможет;удачи!