TypeScript: TypeScript won't import JSDoc types from .js file in node_modules
TypeScript Version: 3.5.2
I use JSDoc type definitions in my JavaScript and use checkjs
in VSCode to have TypeScript type check my JavaScript live. This generally works very well. However I have noticed one failure. TypeScript fails to properly understand JSDoc types when they’re imported from another file. Let me explain.
I have a node module which uses JSDoc to define types. Some of the types are defined in one file and imported into other files using the format:
// vnode.js
/**
* @typedef {Object.<string, any> | {}} Props
* @property {Children} Props.children
*/
/**
* @typedef {VNode[]} Children
*/
/**
* @typedef {string | number | Function} Type
* @typedef {number | string | null} Key
* @typedef {Object.<string, any>} VNode
* @property {Type} VNode.type
* @property {Props} VNode.props
* @property {Children} VNode.children
* @property {Element} VNode.element
* @property {Key} [VNode.key]
* @property {number} VNode.flag
*/
This type VNode
gets imported in another file for use as a parameter of a function:
// render.js
/**
* Render a functional component. The first argument is the component to render. This can be either a JSX tag or an `h` function. The second argument is the container to render in. An optional third argument is an element in the document loaded from the server. This will be hydrated with the component provided to render. This third argument can be a DOM referece for that element, or a valid string selector for it.
* @typedef {import('./vnode').VNode} VNode
* @param {VNode} VNode
* @param {Element | string} container
* @param {Element | string} [hydrateThis]
* @return {void} undefined
*/
In the module itself this works as expected. The type VNode
is understood everywhere it is used.
The problem is when I create a project the uses this module. For some reason TypeScript treats the imported JSDoc type as any
. I’m currently using VSCode Version: 1.37.1. When i was using 1.36.x I was getting the following warning for the import JSDoc type (sic):
TypeScript cannot find a `d.ts` file for `./vnode`. As such its types will be treated as `any`.
What I don’t understand is why would TypeScript be looking for a d.ts
file for a JavaScript file using JSDoc comments for types? Does it know that an import in a JSDoc comment should point to a JSDoc type definition? It seams like when TypeScript sees the @typedef
import statement, it assumes its a normal JavaScript import and starts looking for a d.ts
file. Which leads to it never importing the JSDoc type.
Of course I could copy and paste the type definition everywhere I need it. However that leads to maintenance problems when I need to update the type definition.
Since this works inside the module importing JSDoc types from other files, why can’t it work when the module is imported into another project?
To show the problem here are some images illustrating a module with a render
function. In the first image you can see that the imported VNode
type is being imported and interpreted correctly:

In the next image you can see that this imported type is being interpreted correctly as a parameter of the render
function when this is defined:

But here, when the render
function is imported into a project, the imported type is treated by TypeScript as any
:

To reproduce this problem do the following.
npx @composi/create-composi-app -n Type-Test
The above will creaate a new project on your desktop named Type-Test
. cd
to the new project and run:
npm install
After the dependencies are installed, open the project in VSCode and go to the src/js/app.js
file. Hover over the imported render
function at the top of the page. You’ll see Intellisense pop up, but the first argument for VNode will be of type any
. This is wrong.
Now open the project’s node_modules
folder and go to node_modules/@composi/core/src/render.js
Scroll down to the defintion of the render
funcion. Hover over it or its first parameter. You’ll see that here TypeScript is able to correctly understand the imported type as VNode
, not as any
.
It seams TypeScript is treated JSDoc import statements as if they were JavaScript imports. It then looks for a .d.ts
, which doesn’t exist because this is JavaScript with JSDoc comments. So it defaults to any
.
Interestingly, in earlier versions of VSCode, if I opened the module source code first and hovered over the imported type, this would force VSCode and TypeScript to recognize the type, even in the project where the modules was imported. Currently this no longer works, instead treating such imported JSDoc types as any
no matter what you do.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 16
- Comments: 16
Please, reopen the issue. Thumbs up this message if you want it to be reopened. Thanks.
For anyone else who has found this issue after it closed, hopefully this helps:
Same Problem
I have the same problem as @rbiggs. I’ve created a module called
@imin/shared-data-types
. This module uses almost entirely JSDoc to define its TypeScript types. Within the module, all the files are able to use the types correctly. VSCode recognises if there’s a type error and so doestsc
. That’s great ✔️However, as soon as I’m importing this code to another project, the types disappear. VSCode and
tsc
both recognise the types asany
❌Hope?
@phaux 's solution (https://github.com/microsoft/TypeScript/issues/33136#issuecomment-528657072) was very helpful to me. I set my tsconfig.json to:
And VSCode and
tsc
were finally able to recognise the types!Before:
After:
Wonderful ✔️
The only problem is that when I ran
tsc
I was shown a LOT of new errors.This is because now every single import is being type checked. Many JavaScript libraries come a-cropper when examined under TypeScript’s inscrutable gaze. Fair enough! But I don’t have time to fix all these issues with external libraries. ❌
What I want is for TypeScript to just check the modules that I made that I know have been typed properly using JSDoc. How can I do that?
The solution
Just add the modules to
tsconfig.json
’sinclude
list. e.g.:This works! ✔️ ✔️ ✔️
Typescript version: 3.6.2
This issue should be re-opened given that TypeScript does not resolve JS files with JSDoc from node_modules.
All proposed solutions in this issue are workarounds. Using the TypeScript compiler to emit declaration files is redundant, given that the JS files contain the declaration already. Moreover, I experienced:
All of those issues would exist if declaration files wouldn’t be required for JSDoc JS files.
There’s an option in TS compilerOptions called
maxNodeModuleJsDepth
and it’s set to some low value by default. Try setting that to a higher value.I think TypeScript should simply support
main
andtypes
fields inpackage.json
being set to the same file if the file is JS with JSDoc:Last time I checked it didn’t work. Project which imports such module doesn’t get the types.
Yup. You do need the latest TypeScript installed somewhere, either locally or globally. Latest version is 3.7.5. Set up your
tsconfig.json
file. Then setup an NPM script to run TypeScript to check you files. With this above options in thetsconfig
file it will also create thed.ts
files for your JavaScript. That means you don’t have to worry about TypeScript importing you JSDoc types properly. It will always find and parsed.ts
files. Here’s a link to atsconfig
file for one of my Gitub repos: https://github.com/composi/core/blob/master/tsconfig.jsonActually, @lukehesluke, that’s what I wound up doing as well, using
include
to tell TypeScript to include my JavaScript modules with JSDoc comments. One thing I also had to do what make sure the main index file imported any types being used in sub-files so that when TypeScript hit the index.js file for the module, it also found the path to the types.However, now I no longer bother with any of that because TypeScript now supports creating
d.ts
files from JavaScript files with JSDoc comments. So when I runtsc
, it automatically generates.d.ts
files for everything. This solves the problems that TypeScript still has with following the paths of types defined with JSDoc in complex JavaScript modules. Well, it doesn’t fix the issue. It gets around the problem because by default TypeScript always looks ford.ts
files. Since my modules are written in JavaScript with JSDoc comments, there weren’t any. Now I can let TypeScript create them for me while its also type checking my JavaScript.You just need to update your projects
tsconfig.json
file for this to work. Here’s what I’m currently using for compiler options:Note that I tell TypeScript to output the
d.ts
files into a folder called types. Then in mypackage.json
file I declare that as the location for the project’s types:With these updates to my JavaScript projects, TypeScript will find the
d.ts
files for my JavaScript projects. And these are created by TypeScript based on my JSDoc comments. Win win.I guess I should mention how I check my types and produce the
d.ts
files. Because I have all the instructions for TypeScript in the project’stsconfig.json
file, all I have to do is run a simple npm script which I define in mypackage.json
:Then in the terminal I just run
npm run tsc
. This runs a type check on the JavaScript using my JSDoc comments and then producesd.ts
files as well.Fast forward to 2024 and it still doesn’t work and issue is still closed… nothing is fixed here, just hacky and annoying workarounds in sight.
I tried using a
d.ts
like you suggested to point TypeScript toward the type definitions, but that did not work. Instead, this is what works for me. I have the following expected exports in the module’sindex.js
file, followed by@typedef
pointing to the type definitions in JSDoc that I want TypeScript to be aware of globally:This allows the user to import
h
,render
, etc. and get the correct types in Visual Studio Code.I have found a solution that seem to work well: move
index.js
tosrc/
folder, and createindex.d.ts
at root that point to it: