Types in JavaScript
JavaScript is a loosely typed and dynamic language. Variables in JavaScript are not directly associated with any particular value type, and any variable can be assigned (and re-assigned) values of all types:
let foo = 42; // foo is now a number
foo = 'bar'; // foo is now a string
foo = true; // foo is now a boolean
Intellisense in VS Code
Visual Studio Code's intellisense will only work, if it understands the type
of of your code.
In above example, after you write first line, let foo = 42;
it will show you methods of a number:
But what if you assign a JSON, which is going to hold many properties like id
, createdOn
, etc.
It's also working fine. But, it's unlikely that your variable is going to hold values with initialization. So, now if you check for blank JSON, intellisense will stop working, because now VS code doesn't know the types.
Without proper intellisense, we often make typos, call the method which doesn't exist or even try to access the properties of an objects by a random guess.
To handle such and more complex scenarios, and make sure intellisense works right for those, we will use JSDoc's @param
, @type
and @typedef
block tags.
JSDoc to the rescue
JSDoc comes with lots of tags, you can checkout them all on it's website: https://jsdoc.app/. But for this article, we are going to focus on below 3 tags:
@param
The
@param
tag provides the name, type, and description of a function parameter.The
@param
tag requires you to specify the name of the parameter you are documenting. You can also include the parameter's type, enclosed in curly brackets, and a description of the parameter.
Let's look at some examples.
/**
* @param {string} somebody
*/
function sayHello(somebody) {
alert('Hello ' + somebody);
}
After above code, VS code's intellisense will work great whenever you try to call sayHello
:
You can look at more examples at https://jsdoc.app/tags-param.html#examples.
@type
The
@type
tag allows you to provide a type expression identifying the type of value that a symbol may contain, or the type of value returned by a function. You can also include type expressions with many other JSDoc tags, such as the@param
tag.A type expression can include the JSDoc namepath to a symbol (for example,
myNamespace.MyClass
); a built-in JavaScript type (for example,string
); or a combination of these. You can use any Google Closure Compiler type expression, as well as several other formats that are specific to JSDoc.
Let's take a look at example:
/** @type {Array} */
var foo;
For above code, typing foo.
will load all Array
's properties and methods:
More examples at https://jsdoc.app/tags-type.html#examples
@typedef
The
@typedef
tag is useful for documenting custom types, particularly if you wish to refer to them repeatedly. These types can then be used within other tags expecting a type, such as@type
or@param
.
This tag is really helpful, it helps us to shape any complex type. Let's take a look at example.
This example defines a more complex type, an object with several properties, and sets its namepath so it will be displayed along with the class that uses the type. Because the type definition is not actually exposed by the function, it is customary to document the type definition as an inner member.
// src/toast.js
/**
* @typedef {Object} Toast
* @property {string} id
* @property {boolean} closed - Indicates whether user has close the toast.
* @property {Date} generatedOn - Indicates when the toast was generated.
* @property {string} message - toast content.
* @property {"warn" | "info"} type - Indicates type of toast.
* Also useful to show different icons.
*/
/**
* A function for showing toast
* @param {Toast} toast - {@link toast} object
* containing all components of the toast.
*/
export function showToast(toast) {}
Here is the breakdown of above code:
- The first line:
- We first indicated that we want to create a custom type using
@typedef
tag - Then we indicated that it's going to be an
Object
. You can also create simpler custom type using primitive date types, for examplestring
ornumber
. - And lastly, we named this type as
Toast
- We first indicated that we want to create a custom type using
- Now, as
Toast
is going to be anObject
, in rest of the comments, we defined what are it'sproperties
going to be using@property
tag. You can learn more about@property
tag here.
Now if you try to call showToast
, VS code will do it's magic:
But, this is not enough. In practical scenarios, you would be generating Toast
s in different files and calling showToast
from there. You can export and import showToast
in other files, but what about Toast
type definition?
You can also import type definition the same way you import bindings from another module. But as types are created in comments, you need import them in comments:
// src/home.js
import { showToast } from "./toast";
/**
* @returns {import("./toast").Toast[]}
*/
function getToasts() {}
const allToasts = getToasts();
allToasts.forEach((toast) => {
showToast(toast);
});
Just to emphasis, here's how we imported Toast
type definition:
/**
* @returns {import("./toast").Toast[]}
*/
You can read more about @typedef
at https://jsdoc.app/tags-typedef.html.
Conclusion
We learned how JSDoc block tags, @param
, @type
and @typedef
can help us to achieve maximum out of VS Code's intellisense and code faster without getting into un-wanted issues.
That's it! Thanks for reading. Let me know your thoughts and feedbacks in comments section.
And yes, always believe in yourself š
Photo by Joshua Earle on Unsplash
Top comments (3)
Some real world application, developers cannot decide to change project from JS to TS, For example I am working on JS project 1M+ LOC (lot of app) and there is no option for us to change to TS ( at moment )
why not use typescript
Yeah, you can use typescript.