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
Regression: Rest parameter typed as union of tuple types behaves differently in 4.3Β #44093
classEventEmitter<ET>{// works with overloads// off<K extends keyof ET>(...args: [K, unknown]): void// off<K extends keyof ET>(...args: [K | unknown]): voidoff<KextendskeyofET>(...args: [K,unknown]|[K|unknown]):void{}}functiononce<ET,TextendsEventEmitter<ET>>(emittingObject: T,eventName: keyofET,callback: (event: unknown)=>void,): void{emittingObject.off(eventName,callback);/* fails*/// type assertion works as wellemittingObject.off(eventNameaskeyofET,callback);}
π Actual behavior
The call emittingObject.off(eventName, callback); fails with Argument of type '[string | number | symbol, (event: unknown) => void]' is not assignable to parameter of type '[unknown] | [keyof ET, unknown]'.
It looks like something's gone wrong here where we construct a contextual type for argument position 0 (correct) and then decide to use the presence of that to devolve eventName from keyof ET to its constraint string | number | symbol (bad). But it's something specific to the tuple syntax, since equivalentish forms like off<K extends keyof ET>(args: K | boolean, a2: string | number):void {} don't trigger this.
This is caused by #43183. The issue is that the contextual type of the first argument to off is K | unknown, which ends up reducing to just unknown. Since unknown contains no generic types we think it is safe to reduce keyof ET to its constraint string | number | symbol which would be a candidate for narrowing by CFA. The fix is to ensure K | unknown doesn't get reduced. We do that elsewhere for contextual types, just not in the particular scenario of arguments to rest parameters with union types.
Reacted by Kubilay Kahveci and Titian Cernicova-Dragomir
Activity
RyanCavanaugh commentedon May 14, 2021
Trimming off the last little bit of fat:
It looks like something's gone wrong here where we construct a contextual type for argument position
0(correct) and then decide to use the presence of that to devolveeventNamefromkeyof ETto its constraintstring | number | symbol(bad). But it's something specific to the tuple syntax, since equivalentish forms likeoff<K extends keyof ET>(args: K | boolean, a2: string | number):void {}don't trigger this.ahejlsberg commentedon May 17, 2021
This is caused by #43183. The issue is that the contextual type of the first argument to
offisK | unknown, which ends up reducing to justunknown. Sinceunknowncontains no generic types we think it is safe to reducekeyof ETto its constraintstring | number | symbolwhich would be a candidate for narrowing by CFA. The fix is to ensureK | unknowndoesn't get reduced. We do that elsewhere for contextual types, just not in the particular scenario of arguments to rest parameters with union types.dragomirtitian commentedon Jun 2, 2021
Daniel Rosenwasser (@DanielRosenwasser) Ryan Cavanaugh (@RyanCavanaugh) This does not appear fixed in 4.3.2 (Playground Link) but works in nightly (Playground Link). Will there be a 4.3.3 where this will be included ?