-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Probable bug: Contextual inference and intellisense at call sites buggy in case of circular type parameter constraints like T extends M<T>
#44821
Comments
T extends M<T>
Workaround. Although it doesn't work in my local more complex scenario, so will have to find another minimal repro where it fails. declare const m: <T extends M<T>>(m: T) => T
type M<Self, K = Exclude<keyof Self, "k" | "w">> =
{ a?: number
, b?: number
, c?: number
, d?: number
, k?: { $: K }
, w?: { $: "a" }
}
declare const $4:
< R
, T extends (R extends { $: infer X } ? X : never)>
( t: T) => R & { $: T }
m({
a: 1,
b: 2,
k: $4("a"), // compiles: yes, completions: yes
w: $4("a") // compiles: yes, completions: yes
}) |
I think my comment in #44879 applies equally in this scenario -- It's a fairly normal occurrence for completions to show more options than might be strictly legal, since you might be "in the middle" of writing something and need to provide several completed items before the surrounding call becomes valid (the set of outcomes of which we have no ability to predict within a few hundred milliseconds).
|
Agreed (others are workarounds). I think it'd make sense that the user gets completions for the first parameter of Here's a real world example... Playground. createMachine({
context: { foo: 1 },
entry: {
type: "xstate.assign",
exec: c => ({ foo: c.foo + 1 })
}
})
createMachine({
context: { foo: 1 },
entry: assign(c => ({ foo: c.foo + 1 })) // c is any but should be { foo: number } instead
})
declare const createMachine: <C>(defintion: Machine<C>) => {}
type Machine<C> = {
context: C,
entry: {
type: string,
exec: (context: NoInfer<C>) => unknown
}
}
declare const assign: <C>(f: (context: NoInfer<C>) => NoInfer<C>) =>
{ type: "xstate.assign", exec: (context: C) => NoInfer<C> }
type NoInfer<T> = [T][T extends any ? 0 : never]; Also Ryan may I take it as a compliment that these two issues were (afaict) left for you to triage and label? :P Or they are so ridiculous that no one understood? :') xD |
If you want inference here, I'd remove the |
Here's a version with no This was only a corollary example I gave, my initial question "Am I wrong in thinking this?" is much simpler... Would it be wrong to expect "a" and "b" get suggested via completion for the reasons I gave above when user writes (Reason for initial version - In |
This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
@RyanCavanaugh senpai could I get your word on this please? Because I'm still not sure what is the intended behaviour π Thanks for your time! |
@devanshj sorry, been snoozing on this notification because I knew it'd be interesting (usually implies time-consuming, but not this time). I would consider the linked repro there likely to be a bug; the contextual inference candidate for the |
No problems! and done. Though, Ryan, I'm asking this for the third time xD - I expect "a" and "b" to be suggested in completions, would that be wrong to expect? Here's the most minimal repro... Afaict the assign issue and this are a consequence of the same bug. declare const m: (x: { a: X, b: X }) => {}
type X = { $: "foo" | "bar" }
declare const $: <T>(x: T) => { $: T }
m({
a: { $: "foo" },
b: $("")
// ^|
// expected completions: foo, bar
// actual completions: none
}) My very naive analysis is that to calculate type of first parameter of |
I don't have a way to describe "expected" completion results; completions are either incorrect (e.g. we show you a value that wouldn't work, which is nearly always a bug) or just "would be nicer if it happened more often" (e.g. we fail to show a list at all, which is not necessarily a bug). During an in-fact illegal call (which usually means inference has failed), we apply some heuristics in the language service to try to piece together what we think values that might make the call actually legal. If the inference needed is too complex, none of those heuristics will apply, and there won't be completions as a result. These are tricky to tweak but if you have a PR that fixes it, we'd probably merge it. I wouldn't consider this case to be a per se defect because inference has failed and without a working inference, we can only rely on the salvaging heuristics, which will always be imperfect. |
Thanks for the detailed explanation. Though I realized I missed an important nuance in my question. What I meant to ask was - Is it wrong to expect the type parameter to be inferred as It does work with |
Bug Report
π Search Terms
Circular type parameter constraint, intellisense, call site inference, contextual inference
Related - #44428 #40439
π Version & Regression Information
Tested with TS 4.3.4
β― Playground Link
Playground
π» Code
π Actual behavior
See code comments
π Expected behavior
All three cases or at least
$2
and$3
should compile and user should be able to see completions "a" and "b" for the first parameter of$*
functionsIt seems to be bug to me especially because in case of
k
for$2
and$3
, you do get the completions and even the tooltip shows correct type parameters, but somehow the return type isn't the one thats expected and it doesn't compile AND it works totally fine withw
.The text was updated successfully, but these errors were encountered: