Skip to content

Optional properties in inferred mapped type causes auto-completion to break. #49547

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
tippfelher opened this issue Jun 14, 2022 · 3 comments
Labels
Bug A bug in TypeScript Domain: Completion Lists The issue relates to showing completion lists in an editor Help Wanted You can do this
Milestone

Comments

@tippfelher
Copy link

tippfelher commented Jun 14, 2022

Bug Report

Optional properties in inferred mapped type causes auto-completion to break.

πŸ”Ž Search Terms

Inferred mapped type with optional properties no auto-complete

πŸ•— Version & Regression Information

I believe it never worked.

⏯ Playground Link

https://tsplay.dev/mpnLBw

πŸ’» Code

type A1<T extends object> = {
  [K in (keyof T | 'autoCompleteProp')]:
    K extends 'autoCompleteProp'
    ? boolean
    : K extends keyof T
    ? T[K] extends object
    ? A1<T[K]>
    : object
    : never;
}

type A2<T extends object> = {
  [K in (keyof T | 'autoCompleteProp')]?: // <---- this is the only difference compared to A1
    K extends 'autoCompleteProp'
    ? boolean
    : K extends keyof T
    ? T[K] extends object
    ? A2<T[K]>
    : object
    : never;
}

function makeA1<T extends A1<T>>(t: T) {
  return t;
}

function makeA2<T extends A2<T>>(t: T) {
  return t;
}

makeA1({
  autoCompleteProp: true, // <-- you CAN autoComplete 'autoCompleteProp' here
  bla: {
    autoCompleteProp: true, // <-- you CAN autoComplete 'autoCompleteProp' here
    blubb: {
      autoCompleteProp: false, // <-- you CAN autoComplete 'autoCompleteProp' here
      bleh: {
        // <-- you CAN autoComplete 'autoCompleteProp' here
      }
    },
  },
});

makeA2({
  autoCompleteProp: true, // <-- you CAN autoComplete 'autoCompleteProp' here
  bla: {
    // <-- you CAN'T autoComplete 'autoCompleteProp' here
    blubb: {
      // <-- you CAN'T autoComplete 'autoCompleteProp' here
      bleh: {
        // <-- you CAN'T autoComplete 'autoCompleteProp' here
      },
    },
  },
});

πŸ™ Actual behavior

No auto-completion offered by editor or IDE in marked lines when pressing CTRL + SPACE or a

Edit: You can enter autoCompleteProp: '', in the lines where auto-completion fails. The compiler will complain that autoCompleteProp must be a boolean. Thus, the information about the optional property is there, it's just that it never appears in the auto-completion results.

πŸ™‚ Expected behavior

Auto-complete suggestion for "autoCompleteProp" in every line marked with <--

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript Help Wanted You can do this Domain: Completion Lists The issue relates to showing completion lists in an editor labels Jun 15, 2022
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jun 15, 2022
@Andarist
Copy link
Contributor

I think this has bitten me in the past and it would be lovely to get this fixed.

So far this is what I've gathered:

  1. the origin of the problem can be narrowed down to those lines
  2. with required properties the inferredType (eg: { autoCompleteProp: true; someProp: {}; }) doesn't pass the compareTypes check and thus the instantiatedConstraint (A1<{ autoCompleteProp: true; someProp: {}; }>) gets assigned to inference.inferredType
  3. with optional properties the same inferredType ({ autoCompleteProp: true; someProp: {}; }) passes the compareTypes check and thus inferredType stays assigned to inference.inferredType as-is.
  4. the compareTypes check is simply using compareTypesAssignable under the hood so the logic here makes sense - with optional members the inferredType is assignable to the instantiatedConstraint but with required ones it isn't.
  5. getCompletionsAtPosition ends up looking up properties of this inferredType and it fails to see autoCompleteProp there because the inference "locked" the type param to an object without this property

Part of the question here is - should this autoCompleteProp?: boolean be part of the inferred type? If we assume that makeA1/makeA2 are really identity functions - should this property be available on the returned object? I guess the choice here is somewhat arbitrary and there is no good/bad answer here. A decision had to be made about this and it's not part of that type.

That raises an issue with the autocomplete though. I knew about some other, somewhat similar, issues with "locked" inferences - eg. this PR by @andrewbranch. So I've thought that maybe a similar approach could be taken here. We could avoid "locking" the inference when resolving those types for the completions "request" .

I've experimented with this a little bit and, while I broke some stuff, I think I also got somewhat promising results here. I learned that it's definitely not as easy as just using instantiatedConstraint over inferredType πŸ˜…

However, the problem I see with this is that the mentioned compareTypes is concerned with type params/arguments but what I would truly like to do is to avoid locking the inference for the object at a given position, a mapped type like this can be nested somewhere in the parameter and doesn't have to appear "at the top-level" of the parameter. Does anyone have any ideas how to approach this?

@barak007
Copy link

I hope this gets fixed soon.
I have a simple case similar to this and I think the offender here is the combination of extending Partial and keyof T.

it also sounds similar to #45617

Playground

@victorgarciaesgi
Copy link

Hey! In Typescript 5.8.x this is still happening, despite the #48410 that was merged.

Here is a reproduction

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Bug A bug in TypeScript Domain: Completion Lists The issue relates to showing completion lists in an editor Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

5 participants