Repository navigation
Inference from multiple signatures produces unknown instead of any #31189
Description
Activity
Well,
unknownis the betteranytype, and this would be the expected behaviour with #27265.sandersn commented
on May 21, 2019 MemberAuthorMore actionsUpdate: here's a more isolated repro that removes the dependency on
Array. You also needfilterto be overloaded to getanybefore #30637. Pre-#30637, we don't take the first overload at all, and the second overload is what givesany[]for the return type offilter.interface Bullean { } interface BulleanConstructor { new(value?: any): Bullean; <T>(value?: T): value is T; } interface Ari<T> { filter<S extends T>(callbackfn: (value: T) => value is S): Ari<S>; filter(callbackfn: (value: T) => unknown): Ari<T>; } declare var Bullean: BulleanConstructor; declare let anys: Ari<any>; var xs: Ari<any>; var xs = anys.filter(Bullean)
Wesley Wigham (@weswigham) Could this be because T ⇏ {} but T ⇒ unknown? I thought our rules for that assignability from type parameters were also incorrect until this change, but maybe not.
DanielRosenwasser commented
on May 21, 2019 MemberMore actionsThis change can technically fix this specific occurrence of the problem
interface Bullean { } interface BulleanConstructor { new(value?: any): Bullean; - <T>(value?: T): value is T; + <T extends any>(value?: T): value is T; }My hunch is that there's a weird interaction between the subtyping pass for overload resolution, the changes of implicit bounds, and...something else?
If I had to guess, it's because everything is a proper subtype of
unknown, but our empty object assignability thing for unconstrained type parameters was an assignment-only hack, so didn't affect subtype relation checks, so didn't affect the subtype pass of overload resolution. Now, with anunknown-d signature, there's a signature that can pass in the subtype pass, so we never get to the assignability pass (where we'd choose the any'd signature, since it's first).sandersn commented
on May 21, 2019 MemberAuthorMore actionsNone of those steps are incorrect then. Sigh. In fact they're better because there are fewer hacks.
OK, I'm going to:
- Confirm that this is actually what's happening.
- See whether this could affect any other parts of the DOM.
- Make the change to Boolean's type parameter constraint and see whether it hurts DT/RWC/user test results.
Make the change to Boolean's type parameter constraint and see whether it hurts DT/RWC/user test results.
Urrrr.... I'd not consider that a good fix, in the context of #29571 . Reordering the overload would be better, imo.
sandersn commented
on May 21, 2019 MemberAuthorMore actionsReordering the overloads of
Bulleandoesn't help in the example. Did you mean the overloads ofAri? Or would the real Boolean and Array overloads be different than the miniature example?sandersn commented
on May 21, 2019 MemberAuthorMore actionsSo far, both master and release-3.4 find their respective signatures on the subtype pass.
Oh, right, reordering the overloads isn't going to help the subtype thing. Just change the constructor parameter type from
anytounknowninstead, maybe?sandersn commented
on May 21, 2019 MemberAuthorMore actionsnop
DanielRosenwasser commented
on May 21, 2019 MemberMore actionsIf I had to guess, it's because everything is a proper subtype of
unknown, but our empty object assignability thing for unconstrained type parameters was an assignment-only hack, so didn't affect subtype relation checks, so didn't affect the subtype pass of overload resolutionI'm not sure if the phrasing here is actually alluding to something else, but if there's a hacky assignability check we removed against unconstrained type parameters, why did we remove it?
sandersn commented
on May 21, 2019 MemberAuthorMore actions- Yes, the problem is that
any⇒unknownbutany⇏{}. Ugh I need a better operator for "is related to" that distinguishes subtype-related versus assignment-related. Here, let's use a caret:
any⇒^unknownbutany⇏^{}Edit: in response to Daniel Rosenwasser (@DanielRosenwasser), this isn't really a constraint of a type parameter, it's an instantiation of it (
Boolean<T>(value: T): value is Tgets instantiated withT=anybased on inference from filter'scallback: (value: T) => value is S). The special case in assignability never got hit anyway; we just chose the second overload of filter during the subtype pass.- Haven't checked for other overload patterns yet.
- The additional overload to filter fixes this problem without breaking anything in DT/RWC/user tests.
- Yes, the problem is that
sandersn commented
on May 21, 2019 MemberAuthorMore actionsUpdate.
- Only other overload pattern in es5.d.ts at least is ReadonlyArray.filter.
- I mistakenly changed ReadonlyArray.filter, so now I need to re-run with Array.filter changed and see what breaks. It's considerably more than 0 this time.
- addedFixedA PR has been merged for this issueA PR has been merged for this issue
on May 22, 2019 - locked as resolved and limited conversation to collaborators
on Oct 21, 2025
#30637 breaks webpack in 3 places in a method chain starting with
anys.filter(Boolean).reduce(.... Discovered by the user tests.Note that this stops happening when there is only one signature. As soon as there are multiple signatures of either kind, it repros.
Code
(Edit: switched to isolated repro)
h/t Ryan Cavanaugh (@RyanCavanaugh) for the type name
BulleanExpected behavior:
xs : any[]Actual behavior:
xs : unknown[]