Тьфу, да, я вижу, что вывод по умолчанию не работает достаточно глубоко, чтобы развернуть Iterable<Iterable<T>>
в T
. Это не , что удивительно, если вы посмотрите, как наборы для Iterable
определены в соответствующей библиотеке :
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
Iterable<T>
имеет символ метод с ключом, тип возвращаемого значения Iterator<T>
, который сам по себе определен как:
interface Iterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
, где все методы возвращают IteratorResult<T, ...>
, что определено быть дискриминационным типом объединения
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
, чьи члены являются
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
, из которых только у одного есть свойство value
соответствующего типа T
ты пытаешься найти.
Таким образом, чтобы преобразовать тип X
из Iterable<T>
в T
, компилятору нужно сделать что-то вроде Extract<ReturnType<ReturnType<X[I]>['next']>, { done?: false }>['value']
(где I
- это фиктивное индексируемое свойство [Symbol.iterator]
, которое мы можем пишите сами из-за ошибки в TypeScript; microsoft / TypeScript # 24622 ).
Я думаю, что, возможно, существует некоторый предел глубины, после которого компилятор прекращает попытки делать выводы. Вы можете видеть, что вывод T
из Iterable<T>
работает (возможно, приблизительно 5 или 6 слоев вложенности), но вывод T
из Iterable<Iterable<T>>
слишком глубок (10 или 12 слоев?):
type N = number[][] extends Iterable<infer T> ? T : never; // number[] ?
type O = number[][] extends Iterable<Iterable<infer T>> ? T : never; // unknown ?
Это приводит меня к следующему обходному пути: создайте псевдоним типа, чтобы явно работать с одним слоем Iterable
, а затем дважды используйте его:
type DeIterable<T extends Iterable<any>> = T extends Iterable<infer U> ? U : never;
Вы можете увидеть, как это работает :
type Okay = DeIterable<DeIterable<number[][]>>; // number ?
И теперь flat()
можно определить следующим образом:
function flat<II extends Iterable<Iterable<any>>>(
t: II
): Iterable<DeIterable<DeIterable<II>>> {
return [...t][0]
}
Где входным значением является тип * generic c II
, и мы используем DeIterable
дважды, чтобы получить T
, который вы хотели раньше:
const flatted = flat(iterable) //return Iterable<number[]>
Выглядит хорошо, теперь! Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код