How to get type-checking and generate TypeScript Typing declaration (types.d.ts) from JSDoc annotations
Table of Contents
How to achieve TypeScript-like behaviour in Vanilla JavaScript using JSDoc and @ts-check
in VSCode: that’s the purpose of this post.
TypeScript is a JavaScript superset with types. It solves one of the big problems with JavaScript, which is “what parameters does this function expect?”
TypeScript is great for the JavaScript ecosystem, code written in TypeScript (or with a type declaration file) has better IDE tooling (autocomplete, intellisense) and static checks can be performed.
The great news is that you can achieve the bulk of this without adopting TypeScript at all. If you write JSDoc’s for your JavaScript code, you can get VSCode to typecheck your code and generate TypeScript Typing declaration file (usually with a file name like types.d.ts
).
The full GitHub template repository is available at github.com/HugoDF/jsdoc-type-d-ts-node-pkg.
Type-checking JSDoc-annotated JavaScript code with VSCode and @ts-check
In VSCode you can add a // @ts-check
comment to the top of a file that contains JSDoc annotations and you’ll get type-checking.
For example given the following function/file, you would get an error “Expected 0 arguments, but got 1.” for the ping('foo')
call. Which is explained since ping
’s type declaration/JSDoc doesn’t include any arguments. Similar errors would be generated if the JSDoc included a parameter of a type but the function was called with a parameter of another type.
// @ts-check
/**
* @returns {Promise<string>}
*/
async function ping() {
return 'pong';
}
ping('foo');
module.exports = {
ping
}
You can see the error in VSCode as per the following screenshot.
We’ve now seen how to enable type-checking in a JSDoc-annotated JavaScript source file.
For more advanced JSDoc typings you can check out the JSDoc for the render
function in alpine-test-utils main.js file.
Next we’ll see how to generate TypeScript typing/type declaration file (types.d.ts
) from the JSDoc annotations.
Generating TypeScript typing declaration (types.d.ts
) from JSDoc annotations
In order to generate a TypeScript typing/type declaration file (types.d.ts
) from JSDoc annotations, we’ll use the JSDoc CLI (github.com/jsdoc/jsdoc) and tsd-jsdoc (github.com/englercj/tsd-jsdoc).
You can install them using npm/Yarn:
npm i --save-dev jsdoc tsd-jsdoc
# or with Yarn
yarn add --dev jsdoc tsd-jsdoc
To get them working together we can use the jsdoc CLI as follows.
We use the -t
(or --template
) option to point the JSDoc CLI to tsd-jsdoc -t node_modules/tsd-jsdoc/dist
.
The other options are more straightforward, -r
means we want to recursively parse the files in the src
directory, src
is the origin directory and -d .
mean the destination should be the current directory: .
. We can the run whole thing with npx
.
npx jsdoc -t node_modules/tsd-jsdoc/dist -r src -d .
This command is reasonably unwieldy, we’ll want to alias it in our package.json
as build
in order to run it more easily. See the following package.json update:
{
"// other": "keys",
"scripts": {
"// other": "scripts",
"build": "jsdoc -t node_modules/tsd-jsdoc/dist -r src -d .",
"// more ": "scripts"
}
}
We can then run yarn build
or npm run build
to generate a types.d.ts
file, see the following output (this is what it looks like when your type annotations have changed or types.d.ts
does not exist).
We can also look at the contents of the types.d.ts
file which contains a function declaration for our ping
function and the function signature is: no parameters and a Promise<string>
return value.
declare function ping(): Promise<string>;
We’ve now seen how to generate a TypeScript typing declaration file (types.d.ts
) using JSDoc and tsd-jsdoc.
Next we’ll look at what’s required to publish an npm module with a TypeScript typing declaration file.
Referencing TypeScript typing/type declaration (types.d.ts
) in an npm module
To start off with let’s clarify something that might have been glossed over in the previous section: what’s a typing declaration file?
A typing declaration file is a TypeScript file that defines the interface of the module. It includes only type information. It’s usually called types.d.ts
.
In our example, types.d.ts
declares that our module has a function called ping
and that ping
takes 0 parameters and has a return type of Promise<string>
ie. it’s an async function that takes no parameters and resolves to a string.
When your npm module gets published, for type declarations to be used, you need to leave a reference to the types.d.ts
in your package.json
. The field used to reference type declaration files is types
(although apparently typings
can also be used).
As a quick reminder, we have the following folder structure you can see the full code at github.com/HugoDF/jsdoc-type-d-ts-node-pkg, where the types.d.ts
├── LICENSE
├── README.md
├── node_modules
│ └── ...
├── package.json
├── src
│ └── main.js
├── tests
│ └── ping.js
├── types.d.ts
└── yarn.lock
To reference the types.d.ts
file in package.json
, we can update it to include a "types"
field:
{
"// other": "fields",
"types": "./types.d.ts",
"// more": "fields",
}
That’s it, now when your package (npm module) gets installed, your users will get TypeScript-level support for IDEs (eg. autocomplete, intellisense) and typechecking.
You can find more information about publishing a module with a TypeScript declaration file in TypeScript Documentation - Declaration Files - “Publishing” Section.
We’ve now seen how to publish an npm module with types.d.ts
TypeScript type declaration file that’s generated from JSDoc annotations.
We’ve also seen how to enable type-checking in a JSDoc annotated file.
The full GitHub template repository is available at github.com/HugoDF/jsdoc-type-d-ts-node-pkg.
Photo by Christian Fregnan
Get The Jest Handbook (100 pages)
Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library.
orJoin 1000s of developers learning about Enterprise-grade Node.js & JavaScript