TypeScript: Automatic imports don't work for packages that provide their own types

  • VSCode Version: 1.41.0
  • OS Version: Fedora 30

Steps to Reproduce:

  1. Add a module dependency which includes it’s own types
  2. Start writing out a named export of the added module
  3. No suggestion will be given

Suggestions won’t show until you’ve actually imported the dependency. This is different from how @types modules work, which will show in suggestions without any prior use.

I’ve created an example project

  1. yarn install
  2. open src/main.ts
  3. type use and you will get suggestions for React useCallback, etc.
  4. type watch and you will get no suggestion for Kappi watch

Does this issue occur when all extensions are disabled?: Yes

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 6
  • Comments: 22 (8 by maintainers)

Most upvoted comments

@andrewbranch If this is working as intended then that’s pretty disappointing, because it means libraries with first party type definitions are less user friendly then those who have third party definitions in @types.

Regarding that explanation, you don’t have to look for all .d.ts files in node_modules, you only have to look in the module folders of direct dependencies defined in your projects package.json. (Maybe @RyanCavanaugh can comment)

A further optimization (with some drawbacks) would be to only include the .d.ts defined in the types field of package.json for direct dependencies. This would mean though that nested types such as package-name/foo could not be included since the types field can only link to one file. Unless maybe one main .d.ts imported them, but I haven’t tested that so not sure it works.

@andrewbranch It was reproducible in 3.8.3 from my previous ticket: https://github.com/microsoft/TypeScript/issues/37187#issuecomment-594103856.

We’re using a meta-package that bundles a number of other dependencies for the purpose of managing several applications. Moving all the dependencies in that meta-package back into the project allows those dependencies to be picked up by intellisense (in VSCode). It seems that TypeScript itself shows that it is picking up those dependencies but VSCode itself may be stripping anything that is not present in the project’s package.json. I am guessing Atom is unaffected by this because it is leveraging the full output for TypeScript’s listing.

I will see if I can create a simple reproduction of my specific case.

Thanks for the detailed explanation @andrewbranch. I would still go with @robbiespeed here and say that if I have them in my (first class) package.json, then I will obviously import them at some point and thus nullify the performance argument.

Not just in the same file, but anywhere in the project.

I seem to be having a problem even with the base functionality here… My other ticket #37187 was transferred from VSCode so maybe it’s an actual vscode problem.

VScode:

image With quickfix: image

Other file in same project with the same import: image

Atom:

image

Output from --listFiles filtered for rxjs- looks like it’s picking it up, but not showing as suggestions in vscode:

EXPAND ME

> tsc -p tsconfig.json --listFiles | grep rxjs
/projects/project/node_modules/rxjs/internal/Subscription.d.ts
/projects/project/node_modules/rxjs/internal/types.d.ts
/projects/project/node_modules/rxjs/internal/Subscriber.d.ts
/projects/project/node_modules/rxjs/internal/Operator.d.ts
/projects/project/node_modules/rxjs/internal/observable/iif.d.ts
/projects/project/node_modules/rxjs/internal/observable/throwError.d.ts
/projects/project/node_modules/rxjs/internal/Observable.d.ts
/projects/project/node_modules/rxjs/internal/Subject.d.ts
/projects/project/node_modules/rxjs/internal/observable/ConnectableObservable.d.ts
/projects/project/node_modules/rxjs/internal/operators/groupBy.d.ts
/projects/project/node_modules/rxjs/internal/symbol/observable.d.ts
/projects/project/node_modules/rxjs/internal/BehaviorSubject.d.ts
/projects/project/node_modules/rxjs/internal/ReplaySubject.d.ts
/projects/project/node_modules/rxjs/internal/AsyncSubject.d.ts
/projects/project/node_modules/rxjs/internal/Scheduler.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/Action.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/AsyncScheduler.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/AsyncAction.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/AsapScheduler.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/asap.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/async.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/QueueScheduler.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/queue.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/AnimationFrameScheduler.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/animationFrame.d.ts
/projects/project/node_modules/rxjs/internal/scheduler/VirtualTimeScheduler.d.ts
/projects/project/node_modules/rxjs/internal/Notification.d.ts
/projects/project/node_modules/rxjs/internal/util/pipe.d.ts
/projects/project/node_modules/rxjs/internal/util/noop.d.ts
/projects/project/node_modules/rxjs/internal/util/identity.d.ts
/projects/project/node_modules/rxjs/internal/util/isObservable.d.ts
/projects/project/node_modules/rxjs/internal/util/ArgumentOutOfRangeError.d.ts
/projects/project/node_modules/rxjs/internal/util/EmptyError.d.ts
/projects/project/node_modules/rxjs/internal/util/ObjectUnsubscribedError.d.ts
/projects/project/node_modules/rxjs/internal/util/UnsubscriptionError.d.ts
/projects/project/node_modules/rxjs/internal/util/TimeoutError.d.ts
/projects/project/node_modules/rxjs/internal/observable/bindCallback.d.ts
/projects/project/node_modules/rxjs/internal/observable/bindNodeCallback.d.ts
/projects/project/node_modules/rxjs/internal/InnerSubscriber.d.ts
/projects/project/node_modules/rxjs/internal/OuterSubscriber.d.ts
/projects/project/node_modules/rxjs/internal/observable/combineLatest.d.ts
/projects/project/node_modules/rxjs/internal/observable/concat.d.ts
/projects/project/node_modules/rxjs/internal/observable/defer.d.ts
/projects/project/node_modules/rxjs/internal/observable/empty.d.ts
/projects/project/node_modules/rxjs/internal/observable/forkJoin.d.ts
/projects/project/node_modules/rxjs/internal/observable/from.d.ts
/projects/project/node_modules/rxjs/internal/observable/fromEvent.d.ts
/projects/project/node_modules/rxjs/internal/observable/fromEventPattern.d.ts
/projects/project/node_modules/rxjs/internal/observable/generate.d.ts
/projects/project/node_modules/rxjs/internal/observable/interval.d.ts
/projects/project/node_modules/rxjs/internal/observable/merge.d.ts
/projects/project/node_modules/rxjs/internal/observable/never.d.ts
/projects/project/node_modules/rxjs/internal/observable/of.d.ts
/projects/project/node_modules/rxjs/internal/observable/onErrorResumeNext.d.ts
/projects/project/node_modules/rxjs/internal/observable/pairs.d.ts
/projects/project/node_modules/rxjs/internal/observable/partition.d.ts
/projects/project/node_modules/rxjs/internal/observable/race.d.ts
/projects/project/node_modules/rxjs/internal/observable/range.d.ts
/projects/project/node_modules/rxjs/internal/observable/timer.d.ts
/projects/project/node_modules/rxjs/internal/observable/using.d.ts
/projects/project/node_modules/rxjs/internal/observable/zip.d.ts
/projects/project/node_modules/rxjs/internal/scheduled/scheduled.d.ts
/projects/project/node_modules/rxjs/internal/config.d.ts
/projects/project/node_modules/rxjs/index.d.ts
/projects/project/node_modules/rxjs-compat/Observable.d.ts
/projects/project/node_modules/rxjs/Observable.d.ts
/projects/project/node_modules/rxjs/internal/operators/audit.d.ts
/projects/project/node_modules/rxjs/internal/operators/auditTime.d.ts
/projects/project/node_modules/rxjs/internal/operators/buffer.d.ts
/projects/project/node_modules/rxjs/internal/operators/bufferCount.d.ts
/projects/project/node_modules/rxjs/internal/operators/bufferTime.d.ts
/projects/project/node_modules/rxjs/internal/operators/bufferToggle.d.ts
/projects/project/node_modules/rxjs/internal/operators/bufferWhen.d.ts
/projects/project/node_modules/rxjs/internal/operators/catchError.d.ts
/projects/project/node_modules/rxjs/internal/operators/combineAll.d.ts
/projects/project/node_modules/rxjs/internal/operators/combineLatest.d.ts
/projects/project/node_modules/rxjs/internal/operators/concat.d.ts
/projects/project/node_modules/rxjs/internal/operators/concatAll.d.ts
/projects/project/node_modules/rxjs/internal/operators/concatMap.d.ts
/projects/project/node_modules/rxjs/internal/operators/concatMapTo.d.ts
/projects/project/node_modules/rxjs/internal/operators/count.d.ts
/projects/project/node_modules/rxjs/internal/operators/debounce.d.ts
/projects/project/node_modules/rxjs/internal/operators/debounceTime.d.ts
/projects/project/node_modules/rxjs/internal/operators/defaultIfEmpty.d.ts
/projects/project/node_modules/rxjs/internal/operators/delay.d.ts
/projects/project/node_modules/rxjs/internal/operators/delayWhen.d.ts
/projects/project/node_modules/rxjs/internal/operators/dematerialize.d.ts
/projects/project/node_modules/rxjs/internal/operators/distinct.d.ts
/projects/project/node_modules/rxjs/internal/operators/distinctUntilChanged.d.ts
/projects/project/node_modules/rxjs/internal/operators/distinctUntilKeyChanged.d.ts
/projects/project/node_modules/rxjs/internal/operators/elementAt.d.ts
/projects/project/node_modules/rxjs/internal/operators/endWith.d.ts
/projects/project/node_modules/rxjs/internal/operators/every.d.ts
/projects/project/node_modules/rxjs/internal/operators/exhaust.d.ts
/projects/project/node_modules/rxjs/internal/operators/exhaustMap.d.ts
/projects/project/node_modules/rxjs/internal/operators/expand.d.ts
/projects/project/node_modules/rxjs/internal/operators/filter.d.ts
/projects/project/node_modules/rxjs/internal/operators/finalize.d.ts
/projects/project/node_modules/rxjs/internal/operators/find.d.ts
/projects/project/node_modules/rxjs/internal/operators/findIndex.d.ts
/projects/project/node_modules/rxjs/internal/operators/first.d.ts
/projects/project/node_modules/rxjs/internal/operators/ignoreElements.d.ts
/projects/project/node_modules/rxjs/internal/operators/isEmpty.d.ts
/projects/project/node_modules/rxjs/internal/operators/last.d.ts
/projects/project/node_modules/rxjs/internal/operators/map.d.ts
/projects/project/node_modules/rxjs/internal/operators/mapTo.d.ts
/projects/project/node_modules/rxjs/internal/operators/materialize.d.ts
/projects/project/node_modules/rxjs/internal/operators/max.d.ts
/projects/project/node_modules/rxjs/internal/operators/merge.d.ts
/projects/project/node_modules/rxjs/internal/operators/mergeAll.d.ts
/projects/project/node_modules/rxjs/internal/operators/mergeMap.d.ts
/projects/project/node_modules/rxjs/internal/operators/mergeMapTo.d.ts
/projects/project/node_modules/rxjs/internal/operators/mergeScan.d.ts
/projects/project/node_modules/rxjs/internal/operators/min.d.ts
/projects/project/node_modules/rxjs/internal/operators/multicast.d.ts
/projects/project/node_modules/rxjs/internal/operators/observeOn.d.ts
/projects/project/node_modules/rxjs/internal/operators/onErrorResumeNext.d.ts
/projects/project/node_modules/rxjs/internal/operators/pairwise.d.ts
/projects/project/node_modules/rxjs/internal/operators/partition.d.ts
/projects/project/node_modules/rxjs/internal/operators/pluck.d.ts
/projects/project/node_modules/rxjs/internal/operators/publish.d.ts
/projects/project/node_modules/rxjs/internal/operators/publishBehavior.d.ts
/projects/project/node_modules/rxjs/internal/operators/publishLast.d.ts
/projects/project/node_modules/rxjs/internal/operators/publishReplay.d.ts
/projects/project/node_modules/rxjs/internal/operators/race.d.ts
/projects/project/node_modules/rxjs/internal/operators/reduce.d.ts
/projects/project/node_modules/rxjs/internal/operators/repeat.d.ts
/projects/project/node_modules/rxjs/internal/operators/repeatWhen.d.ts
/projects/project/node_modules/rxjs/internal/operators/retry.d.ts
/projects/project/node_modules/rxjs/internal/operators/retryWhen.d.ts
/projects/project/node_modules/rxjs/internal/operators/refCount.d.ts
/projects/project/node_modules/rxjs/internal/operators/sample.d.ts
/projects/project/node_modules/rxjs/internal/operators/sampleTime.d.ts
/projects/project/node_modules/rxjs/internal/operators/scan.d.ts
/projects/project/node_modules/rxjs/internal/operators/sequenceEqual.d.ts
/projects/project/node_modules/rxjs/internal/operators/share.d.ts
/projects/project/node_modules/rxjs/internal/operators/shareReplay.d.ts
/projects/project/node_modules/rxjs/internal/operators/single.d.ts
/projects/project/node_modules/rxjs/internal/operators/skip.d.ts
/projects/project/node_modules/rxjs/internal/operators/skipLast.d.ts
/projects/project/node_modules/rxjs/internal/operators/skipUntil.d.ts
/projects/project/node_modules/rxjs/internal/operators/skipWhile.d.ts
/projects/project/node_modules/rxjs/internal/operators/startWith.d.ts
/projects/project/node_modules/rxjs/internal/operators/subscribeOn.d.ts
/projects/project/node_modules/rxjs/internal/operators/switchAll.d.ts
/projects/project/node_modules/rxjs/internal/operators/switchMap.d.ts
/projects/project/node_modules/rxjs/internal/operators/switchMapTo.d.ts
/projects/project/node_modules/rxjs/internal/operators/take.d.ts
/projects/project/node_modules/rxjs/internal/operators/takeLast.d.ts
/projects/project/node_modules/rxjs/internal/operators/takeUntil.d.ts
/projects/project/node_modules/rxjs/internal/operators/takeWhile.d.ts
/projects/project/node_modules/rxjs/internal/operators/tap.d.ts
/projects/project/node_modules/rxjs/internal/operators/throttle.d.ts
/projects/project/node_modules/rxjs/internal/operators/throttleTime.d.ts
/projects/project/node_modules/rxjs/internal/operators/throwIfEmpty.d.ts
/projects/project/node_modules/rxjs/internal/operators/timeInterval.d.ts
/projects/project/node_modules/rxjs/internal/operators/timeout.d.ts
/projects/project/node_modules/rxjs/internal/operators/timeoutWith.d.ts
/projects/project/node_modules/rxjs/internal/operators/timestamp.d.ts
/projects/project/node_modules/rxjs/internal/operators/toArray.d.ts
/projects/project/node_modules/rxjs/internal/operators/window.d.ts
/projects/project/node_modules/rxjs/internal/operators/windowCount.d.ts
/projects/project/node_modules/rxjs/internal/operators/windowTime.d.ts
/projects/project/node_modules/rxjs/internal/operators/windowToggle.d.ts
/projects/project/node_modules/rxjs/internal/operators/windowWhen.d.ts
/projects/project/node_modules/rxjs/internal/operators/withLatestFrom.d.ts
/projects/project/node_modules/rxjs/internal/operators/zip.d.ts
/projects/project/node_modules/rxjs/internal/operators/zipAll.d.ts
/projects/project/node_modules/rxjs/operators/index.d.ts

So after some trying with a minimal example, I found my issue to be related to the yield keyword. See https://github.com/microsoft/TypeScript/issues/36933

I need to take my mouse and click on an icon instead of using my keyboard

Use ⌘. (macOS) or Ctrl+. (Windows) to trigger code fixes 😄

It is true that codefixes (lightbulb) for auto imports cover more cases than completions (show up as you type, or triggered manually with ctrl/cmd+space). But, looking at the type definitions for redux-saga, I don’t see why this would be one of those cases. I would definitely call that behavior undesirable, if not an outright bug.

If you have an example that doesn’t exhibit the root cause here, please file a separate issue with a minimal repro so we can easily diagnose it. As a quick check though, make sure the tsconfig contains your files via tsc -p your-tsconfig.json --listFiles

So knowing this, if you want auto-imports to work for non-@types packages, you now know that you first have to resolve, read, parse, and bind all those packages, which is quite a lot of work. What’s more, the only really straightforward way to do that work is to include those files by default in the compilation. But that would be a catastrophically breaking change, since it could potentially introduce new globals and module augmentations into the program that weren’t there before!

If there was an option in tsconfig then it wouldn’t be a breaking change. You wouldn’t even need to make a new option if typeRoots was updated to accept node_modules without errors, instead of error when encountering a package without types, it would need to skip it.

In this case I don’t think there’s any performance concern, because you’re most likely to be importing those modules at some point anyway. And there’s no more of a concern of new globals then there is with @types dependencies.

Perhaps in the language service you could create a secondary side program that contains just those node_modules packages, cache it, and query it for auto-imports. But this would duplicate a lot of work and a lot of memory usage. Maybe you could build up some infrastructure to parse and bind those files without a whole program and type checker, and answer rudimentary questions about them just from their syntax and symbols. I think this is probably the right place to start, but auto-imports does currently use type checker information when building that list, so this extra auto-import list would likely be handled with less accuracy than the list that comes from your active program. Building out the infrastructure to do this would be a fairly significant investment, and it’s hard to predict just how bad the perf impact would be before making that investment.

The idea of a separate process for auto-imports would help solve some other issues. It could allow no types would be included by default, you would need import them. This would help a bunch in instances where globals conflict in sub projects like with DOM and WebWorkers, or globals from testing suites get added to your program. The only way around that kind of issue right now is to explicitly define types in tsonfig, which can be tedious when starting a project and adding dependencies.

For performance issue: Why not allowed users to decide with use of for example tsconfig ???

it’s still going to be a performance hit on completions no matter what

Perhaps I’m misunderstanding something, but doesn’t every @types package also include a performance hit? Say you have 8 @types packages what would really be the performance difference to 8 first party typed packages?

I want to reiterate that once you have a single import from a package anywhere in your project, auto-imports from that package work everywhere in your project.

Yes that’s understood, but imagine this scenario: You just added a dependency and it’s 3rd party types. Of course you don’t have that imported anywhere in your project yet, but auto-import helps you out so you can use that library right away.

Now you add a dependency with 1st party types, you do the exact same thing as before and attempt to use it right away, but this time you can’t. Even though this dependency was made with TypeScript, it has a worse user experience.

you only have to look in the module folders of direct dependencies defined in your projects package.json

This is something that’s been on my mind since #32517. Prior to that PR, we didn’t have an in-memory cache of package.json dependencies. But, it’s still going to be a performance hit on completions no matter what—you’re asking for global completions to do an arbitrary additional amount of work and/or hold an arbitrary additional amount of program data in memory. This request has come up a couple times in the past, and it’s something I’m interested in experimenting with, but based on the relatively low traction on these issues, I’m still skeptical that the performance tradeoff will be worth it.

this is a huge productivity slowdown for me

I want to reiterate that once you have a single import from a package anywhere in your project, auto-imports from that package work everywhere in your project.

@SantoJambit there is kind of already a tsconfig option which is to explicitly define compilerOptions.types, but that means when you add a dependency you need to also add it to the types array.

There’s also typeRoots which you might expect to work if you used "typeRoots": ["node_modules/@types", "node_modules"], but if any package in the defined typeRoots folders don’t include types, then typescript errors.

This is working as intended and/or duplicate of #29629, #30033, and #30474.

Suggestions start appearing once you manually imported at least one type from the module in your file.

Not just in the same file, but anywhere in the project. See Ryan’s explanation here: https://github.com/microsoft/TypeScript/issues/30033#issuecomment-467991747