-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Don’t mix in base constraint completions on indexed access types with type parameter index types #36364
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
Conversation
@typescript-bot pack this |
Heya @DanielRosenwasser, I've started to run the tarball bundle task on this PR at 83ce8a4. You can monitor the build here. It should now contribute to this PR's status checks. |
Not convinced that it is "wrong" to do 😄 |
Nor am I; I just suspect that a being with 5x my intelligence and 10x my time could probably find a better way. |
Hrmmm, so hear me out here - our primary consideration is contextually typed object literal completions; specifically |
@weswigham I looked into either an assignability- or subtype-based approach several months ago, and IIRC, it didn’t work because of how either optional properties or excess properties are treated... but it’s been too long for me to remember specifically why it didn’t pan out. |
Ofc, there's still the possibility of something even more complicated like
where the desired completions for So, thinking out loud here, maybe the problem is attempting to do this with instantiated vs base constraint - with the example above, it seems more like we want to do inference to produce an instantiated signature, but ignoring the parameter we're editing... |
I also think that by the nature of completions, you might get too many false negatives to use type relations—e.g., inference picks the best candidate, but you haven’t yet typed some remaining required members, so the object as it stands now is not assignable/subtype of anything relevant. |
The best way I’ve been able to think about when you want base constraint completions on an object literal is when the object literal affects the inference of its own type. But, that reasoning may still not be quite right, and moreover I couldn’t find a way to know that. |
@typescript-bot pack this |
Heya @andrewbranch, I've started to run the tarball bundle task on this PR at 28cfb4c. You can monitor the build here. It should now contribute to this PR's status checks. |
@andrewbranch try out my patch/branch here. It seems to do what we want and isn't too jank about it. |
@weswigham it doesn’t work when the type parameter has already been inferred because of a different property on the same object argument: |
Hm, yeah, eliding the whole argument while looking for the properties of a nested literal would have that effect. I think the targeted thing we want is eliding just the |
I’m not sure that’s sufficient either. My example is trivially changed to type X = { a: { a }, b: { b } }
function f<T extends 'a' | 'b'>(p: { kind: T } & X[T]) {}
f({
kind: "a",
/**/
}) |
@andrewbranch Or rather, we want to block making inferences to the targeted |
@andrewbranch I have a prototype of the idea here. It needs to cache a little bit more safely (probably by cloning node trees like my prior attempt did), but directly forbidding inference from occurring on exactly the target expression seems to do the trick. |
@weswigham I tried out that commit. There are a couple failing tests, and I’m a little lost as to what’s happening in interface MyOptions {
hello?: boolean;
world?: boolean;
}
declare function bar<T extends MyOptions>(options?: Partial<T>): void;
bar({ hello: true, /*1*/ }); When you have an empty object at the call site There’s a more complicated one with some really strange behavior: interface Test {
keyPath?: string;
autoIncrement?: boolean;
}
function test<T extends Record<string, Test>>(opt: T) { }
test({
a: {
keyPath: 'x.y',
autoIncrement: true
},
b: {
/**/
}
}) As written, you get no completions inside |
I looked over it; it's because we don't do a direct inference from the object literal type to
Ahh, neat; looks like we need to block inference to all object (and array) literals up the spine between the expression under inspection and the surrounding call like expression, too. (Since if we lock in one of their types, we'll lock in the type of the child expression we want to ignore)
I hadn't looked at any yet, since I was just sketching how we might be able to solve the problem, but with the latest revision on that branch, I have, and they now all pass (seems like they just needed the fixes for the two things you brought up, I guess). I've taken the liberty of adding fourslash tests to that branch, too (since debugging a fourslash test is way easier than an editor); which is visible in the branch diff here. |
Superceded by #36556 |
Recall #30507, trying to get completions at a position like:
Since an empty object satisfies the constraint
{ x?: string }
,T
is inferred as an empty object, and therefore getting members of the contextual type at that position yields an empty list. So in #32100, #33937, and #34855, we started instantiatingf
’s call signature with its constraint, getting the type of the argument in that signature, and mixing in properties from that type into the completions list.Unfortunately, that had some unintended side effects when the signature’s parameter types have a non-linear relationship with a type parameter included in the signature, like conditional types and indexed access types that reference one of the type parameters. In real-world complaints from users, every case I’ve seen has been an indexed access type where the index type is a type parameter:
In these cases, having more options is wrong and annoying.
This PR special-cases indexed access types where the index type is a type parameter of the contextual-type-providing signature (with some wiggle room to look past unions and intersections and conditional types).
It’s a band-aid fix, but a) I have spent several days trying to think of something better that’s not wildly expensive, and b) it covers the only failure case that people have actually complained about. That said, I don’t love this and am open to suggestions.
Fixes #35702, #36025