-
Notifications
You must be signed in to change notification settings - Fork 12.8k
generic type parameters, for generic call expressions, not reduced to canonical form, causing type inference malfuction and errors #56702
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Wow, jQuery is still a thing? |
The TypeScript CI tests includes I submitted a fix for #56013 in pull #56373. The bug in #56013 is improperly caching values of generic call expressions, preventing later correct reevaluation of those call expressions with different generic contexts. I just so happens that the bug fix #56373 triggered correct reevaluations that revealed the bug in this current bug report. Just to be clear, this bug report #56702 does not use the code in pull #56373 at all, it is only using already released versions. I've analyzed this pre-existing bug and condensed it down a simple example.
|
@craigphicks To be clear, my comment was a joke - jQuery is very old (2006) and itβs rare that JS middleware remains relevant (let alone actively maintained) for that long! π |
@fatcerberus - npm jQuery still seems to have million of downloads, whereas semantic-ui is only the thousands, and it is particular usage of $() by semantic-ui in which the latent TypeScript bug is revealed as shown here. |
We need a repro of this that doesn't depend on something external as large as jquery |
@RyanCavanaugh - added as an update to original post. Note the accompanying debugger analysis in case it is helpful. |
I am closing this because I am adding a little more debugging analysis. When finished I will repost. |
π Search Terms
Couldn't find any issues that match this one sufficiently.
π Version & Regression Information
This is the behavior in every version I tried up to 5.3.2.
β― Playground Link
playground
π» Code
π Actual behavior
Even though the type declarations for
t1
andt2
are the same (after reduction), onlyt2
shows an error. The same relationship exists betweent11
andt12
.The return type of assigned to
t1
isstring | JQuery<HTMLElement> | undefined
.π Expected behavior
t1
andt2
should have the same error status. Likewise fort11
andt12
.The jQuery function
$()
has a generic signature<TElement=HTMLElement>() JQuery<TElement>
. So the return type assigned tot1
should beJQuery<string> | JQuery<HTMLElement> | JQuery<undefined>
.As shown at the bottom of the code,
JQuery<string>
is judged to be an invalid instantiation of JQuery. If so, the lines witht1
andt2
should both be errors, but they are not. However, the actual declaration for the interface JQuery seems to bewhich actually doesn't look as though it should constraint the domain of JQuery to HTMLElement, so perhaps
JQuery<string>
should not be an error? Furthermore, from within aDefinitelyTyped/semantic-ui
test file,JQuery<string>
does not incur an error.Additional information about the issue
Origin of strange declaration type
I explain where the weird declaration types (e.g.
(string | JQuery<HTMLElement>) & (string | JQuery<HTMLElement> | undefined)
) came from.The following is quoted and condensed from filles under
DefinitelyTyped/semantic-ui-search
The type of
Param["stateContext"]
is indeed the weird type:The error can then be incurred as follows:
playground
Relation to issue #56013 and pull #56373
The submitted pull pull #56373 for the bug in issue #56013 resulted in similar errors to this report. That's because the bug #56013 is due to improper caching of generic call expression arguments, resulting in the call expression not be re-evaluated when the context changed. When the fix allows that later evaluation, those latent errors appear in the existing test code under
DefinitelyTyped/semantic-ui
.An obvious "patchy" fix would be to write new code to force reduction just in time at the critical point - but it could be called multiple times. A better fix would be to do the reduction once when the declaration is parsed - but there is likely some reason it wasn't done that way. Some feedback from the original designers would be helpful.
UPDATE:
Stand alone code with pseudo jQuery
playground
Debugging analyis using the above code for the psuedo-JQuery
In the declaration
the type
is reduced to union of these 4 types
string
JQuery<HTMLElement>
string & JQuery<HTMLElement>
JQuery<HTMLElement> & string
inferTypes
tries to inferTElement
inby independently inferring for
TElement
from each of the 4 types above,to get the following candidates:
string
, target:JQuery<TElement>
TElement
: (none)JQuery<HTMLElement>
, target:JQuery<TElement>
TElement
:HTMLElement
string & JQuery<HTMLElement>
, target:JQuery<TElement>
TElement
:HTMLElement
(repeat, ignored)JQuery<HTMLElement> & string
, target:`JQuery``TElement
:string
Then it takes the union
string | HTMLElement
so the final return type isJQuery<string | HTMLElement>
.The most noticeable problem is the result for:
JQuery<HTMLElement> & string
, target:JQuery<TElement>
TElement
:string
That happens because
inferTypes
callsgetApparentType(JQuery<HTMLElement> & string)
which promotes
string
toString
.Then
JQuery<HTMLElement> & String
is object whose properties are compared to the properties ofJQuery<TElemet>
.They both share
[[Symbol.iterator]]
as a property the the properties(() => { next(): { done: true; value: any; } | { value: HTMLElement; }; }) & (() => IterableIterator<string>)
"() => { next(): { done: true; value: any; } | { value: TElement; }; }"
become a valid source-target pair for inference in the function
Each match potentially contributes a candidates. In this case the deep match stack is as follows:
(...args: [] | [undefined]) => IteratorResult<string, any>
() => { done: true; value: any; } | { value: TElement; }
IterableIterator<string>
{ next(): { done: true; value: any; } | { value: TElement; }; }
IteratorYieldResult<string>
{ value: TElement; }
string
TElement
and so
string
gets added as an inferred candidate forTElement
.There are a couple of remarkable things going on here.
One remarkable thing
Using the the return type of the objects iterator for to infer the typeArgument for the object itself.
By coincide
String
andJQuery
both have iterators, but the inference seems to be an extraordinary overreach.string & <SomeObject>
(SomeObject
exceptingEnum
) tonever
ingetReducedType
. In the rare cases whereString & <SomeObject>
is actually intended to represent their common properties,String
can be explicitly specified instead ofstring
.WeakMap
or using a straightforwardinterface TaggedString<T> {string:string, tag:T}
. Overloading the&
symbol, which already has a specific meaning in flow and inference, to represent other concepts is not a clean design, so TypeScript should not be obliged to support it.Another remarkable thing, that does not directly cause the main topic bug
There are a lot assumptions in
inferFromSignatures
, and because of that these two inferences give difference results;string & JQuery<HTMLElement>
, target:JQuery<TElement>
TElement
:HTMLElement
(repeat, ignored)JQuery<HTMLElement> & string
, target:JQuery<TElement>
TElement
:string
The result should not depend upon the order around the
&
symbol. (However, changes to this "remarkable thing" would not prevent the bug which is the main topic of this post.)tsserver
, and offer to fill in corresponding template constraints as an auto-completion. What I mean is not the final type (which is obviously may be undetermined), but the location in terms of object member path.The text was updated successfully, but these errors were encountered: