Open
Description
Bug Report
π Search Terms
Control flow analysis destructuring assignment
π Version & Regression Information
This was already there, but I thought it might have been fixed with the introduction of this feature in 4.6.
β― Playground Link
Playground link with relevant code
π» Code
export const reduce = <S, T extends S>(self: Iterable<T>, operation: (acc: S, t: T) => S): S => {
const iter = self[Symbol.iterator]();
let { value, done }: IteratorResult<T, unknown> = iter.next();
if (done) throw new Error("Empty iterable can't be reduced.");
let acc: S = value; // Type 'unknown' is not assignable to type 'S'.
while (!done) {
acc = operation(acc, value);
({ value, done } = iter.next());
}
return acc;
};
Note that if you don't upcast iter.next()
, you won't get errors, but not for good reasons:
let {value, done} = iter.next(); // type is IteratorResult<T, any>
if (done) throw new Error("Empty iterable can't be reduced.");
// value is any and can be assigned to anything
A smaller repo would be:
export function foo(iter: Iterator<number, string>) {
let { value, done } = iter.next();
if (done) return;
let acc: number = value; // Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'.
return acc;
}
π Actual behavior
value
above in the last example is inferred as number | string
, it should be number
π Expected behavior
I expect the same inference as without destructuring:
export const reduce2 = <S, T extends S>(self: Iterable<T>, operation: (acc: S, t: T) => S): S => {
const iter = self[Symbol.iterator]();
let result: IteratorResult<T, unknown> = iter.next();
if (result.done) throw new Error("Empty iterable can't be reduced.");
let acc: S = result.value; // result.value is T (extends S)
while (!result.done) {
acc = operation(acc, result.value);
result = iter.next();
}
return acc;
};