You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Control Flow Analysis for Destructured Discriminated Unions doesn't work for let variablesΒ #48071
exportconstreduce=<S,TextendsS>(self: Iterable<T>,operation: (acc: S,t: T)=>S): S=>{constiter=self[Symbol.iterator]();let{ value, done }: IteratorResult<T,unknown>=iter.next();if(done)thrownewError("Empty iterable can't be reduced.");letacc: S=value;// Type 'unknown' is not assignable to type 'S'.while(!done){acc=operation(acc,value);({ value, done }=iter.next());}returnacc;};
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)thrownewError("Empty iterable can't be reduced.");// value is any and can be assigned to anything
A smaller repo would be:
exportfunctionfoo(iter: Iterator<number,string>){let{ value, done }=iter.next();if(done)return;letacc: number=value;// Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'.returnacc;}
π 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:
exportconstreduce2=<S,TextendsS>(self: Iterable<T>,operation: (acc: S,t: T)=>S): S=>{constiter=self[Symbol.iterator]();letresult: IteratorResult<T,unknown>=iter.next();if(result.done)thrownewError("Empty iterable can't be reduced.");letacc: S=result.value;// result.value is T (extends S)while(!result.done){acc=operation(acc,result.value);result=iter.next();}returnacc;};
You forgot to fill out the issue template for bug reports.
CFA for destructured types requires the type to be inferred, but you're explicitly setting the type. Remove the : IteratorResult<T, unknown> part from your variable declaration and it works.
But, the bug is not fixed by leaving out IteratorResult<T, unknown>, see my last comment, it just won't give an error, because it is inferred as any, instead of unknown (second generic parameter is any by default).
You can also try:
functionfoo(iter: Iterator<number,unknown>){let{ value, done }=iter.next();if(done)thrownewError("Empty iterable can't be reduced.");letacc=value;// unknown, not number}
changed the title [-]Control Flow Analysis for Destructured Discriminated Unions doesn't work for IteratorResult[/-][+]Control Flow Analysis for Destructured Discriminated Unions doesn't work for let variables[/+]on Apr 22, 2022
exportconstlast=<T>(self: Iterable<T>): T=>{let{ found, last }: {found: false;last?: undefined}|{found: true;last: T}={found: false,};for(constitofself){({ found, last }={found: false,last: it});}if(!found)thrownewNoSuchElementException("Iterable contains no element matching the predicate.");// can not infer last to be Treturnlast;};
Activity
MartinJohns commentedon Mar 2, 2022
: IteratorResult<T, unknown>part from your variable declaration and it works.kasperpeulen commentedon Mar 3, 2022
Martin Johns (@MartinJohns) Okay, I'm sorry, I will fill it out.
But, the bug is not fixed by leaving out
IteratorResult<T, unknown>, see my last comment, it just won't give an error, because it is inferred as any, instead of unknown (second generic parameter is any by default).You can also try:
[-]Control Flow Analysis for Destructured Discriminated Unions doesn't work for IteratorResult[/-][+]Control Flow Analysis for Destructured Discriminated Unions doesn't work for let variables[/+]kasperpeulen commentedon Apr 22, 2022
I have another example:
Ryan Cavanaugh (@RyanCavanaugh) Martin Johns (@MartinJohns)
whzx5byb commentedon Aug 6, 2022
A shorter
constvslet. Example taken from #46266.