Как вы показали, метод итератора next()
возвращает значение типа IteratorResult
. Это определяется как:
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T>
| IteratorReturnResult<TReturn>;
, объединение из IteratorYieldResult
и IteratorReturnResult
, сами определены , например:
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
Это означает, что компилятор сообщает вам, что iter.next()
возвращает либо IteratorYieldResult
объект со свойством false
/ missing done
и value
типа number
, или он возвращает объект IteratorReturnResult
со свойством true
done
и value
типа any
.
Вы присваиваете это переменной типа IteratorYieldResult
, и компилятор говорит вам: «подождите, это может быть не такого типа. Это может быть IteratorReturnResult
".
Теперь, так как вы сами написали генератор, вы знаете, что при первом вызове next()
вы получите IteratorYieldResult
, потому что есть три оператора yield
до возврата из функции. Но это информация, которой система типов просто не обладает. Это так:
function foo(): number | string {
return "hello";
}
const str: string = foo(); // error! number not assignable to string
Функция foo()
возвращает либо number
, либо string
, в соответствии с сигнатурой типа. Вы можете сказать, что он всегда будет возвращать string
, но компилятор не может этого сказать, потому что он просто смотрит на сигнатуру, а не углубляется в реализацию. И вот он жалуется: «эй, может быть str
будет number
во время выполнения, а не string
, как вы заявляете».
Так что же «правильно» делать Вот? Я думаю, это зависит от того, что вы пытаетесь сделать. В этом игрушечном примере вы можете просто сказать компилятору, что вам не нужно беспокоиться и что вы уверены , чтобы получить IteratorYieldResult
, а не IteratorReturnResult
. И этот вид «сообщения» компилятору о том, что что-то имеет более узкий тип, чем он может проверить, выполняется с помощью утверждения типа :
const itemAsserted = iter.next() as IteratorYieldResult<number>; // okay
Или вы можете выполнить общую проверку где вы не утверждаете, что знаете, что выходит (что имеет смысл, если реализация myIter()
может измениться, или если вы делаете iter.next()
в al oop):
const itemSafe = iter.next();
// const itemSafe: IteratorResult<number, any>
if (!itemSafe.done) {
itemSafe; // const itemSafe: IteratorYieldResult<number>
itemSafe.value.toFixed(); // okay
}
Здесь мы просто позволяем компилятору сообщить нам, что iter.next()
возвращает IteratorResult<number, any>
. Затем мы берем это возвращаемое значение и проверяем , проверяя свойство done
. Если это не true
, то компилятор услужливо сузит результат до IteratorYieldResult<number>
для вас. Вам не нужно ничего утверждать, потому что компилятор понимает тип проверки типов, выполненный выше; это потому, что IteratorResult
является дискриминационным объединением , где можно проверить одно свойство (в данном случае done
), чтобы выяснить, на какого члена объединения вы просматриваете.
Хорошо, я надеюсь, что это имеет смысл для вас и полезно. Удачи!
Детская площадка ссылка на код