Iterable не может подтвердить generi c T в функции - PullRequest
0 голосов
/ 29 апреля 2020

Вот моя проблема.

const iterable = [[[1,2,3]]]
function flat<T>(t:Iterable<Iterable<T>>):Iterable<T>{
    return [...t][0]
}
const flatted = flat(iterable) //return Iterable<unknown>  

выше функция не может предположить, что T как число, просто утвердить его как неизвестное. В этот момент я подумал, что «хм ... generi c в generi c не может быть выведен?». Но код ниже работает хорошо

const iterable = [[[1,2,3]]]
function flat<T>(t:Array<Array<T>>):Array<T>{
    return [...t][0]
}
const flatted = flat(iterable) // omg.. return Array<number> 

также

const iterable = [[[1,2,3]]]
function flat<T>(t:Iterable<Array<T>>):Array<T>{
    return [...t][0]
}
const flatted = flat(iterable) // also works.. return Array<number> 

В чем различия между ними? Спасибо за чтение моего вопроса.

1 Ответ

2 голосов
/ 29 апреля 2020

Тьфу, да, я вижу, что вывод по умолчанию не работает достаточно глубоко, чтобы развернуть 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[]>  

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

Детская площадка ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...