JSDoc, treat POJO as instance of Class - javascript

I have a class and a function that takes instance of that class or a similar POJO object as argument.
I want to annotate this function using JSDoc.
class Test {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
/**
* #param {Test} test
*/
function handleTest(test) {
console.log(test.a, test.b);
}
// Webstorm complains that argument is not of type Test
handleTest({
a: 'this is a'
});
Using #param {Test} test almost works... but WebStorm complains that POJO isn't assignable to type Test.
Is there some JSDoc trick I can do to make it clear that both instance of Test and a Test-like object are both OK?

Use type union:
/**
* #param {Test|{a,b}} test
*/
function handleTest(test) {
console.log(test.a, test.b);
}
The |{a,b} part of the type denotes separate allowable types. You could just use {Test|{}} but then the IDE doesn't know that a and b are expected properties.
That's the shorthand version; you could also define what "Test-like" objects are in case you plan on using and documenting TestLike objects in more than one place:
/**
* #typedef {object} TestLike
* #property {string} a - some property a
* #property {string} b - some property b
*/
/**
* #param {Test|TestLike} test
*/
handleTest(test) {
// ...
}

Related

Type information for a conditional property in JSDoc

I'm adding type information for TypeScript to an existing JavaScript library via JSDoc. I have a constructor function that might set a property based on the value(s) of the parameter(s) it's given:
/**
* Settings you can use to configure an instance of an {#link ExampleClass}
*
* #typedef {Object} ExampleOptions
* #property {true} [option] You can set this to `true` to make a property on an instance of {#link ExampleClass} exist. If you don't set it, that property won't exist
*/
/**
* A thing that does stuff
*
* #constructor
* #param {ExampleOptions} [param] Options you can use to configure the new instance
*/
function ExampleClass(param) {
if(param.option === true) {
/**
* A property that only exists based on the options given to this constructor
*
* #type {boolean}
*/
this.property = true;
}
}
I was hoping that TypeScript would externally interpret the declaration of property to be like property?: boolean;, but it looks like it gets interpreted to be non-optional, and comes up in the autocomplete in my editor without having to check for its existence ahead of time. Ideally, I'd like for it to be an optional property that you'd have to check for before you can use it, or even allow you to use it unchecked if TypeScript can somehow guarantee that you had passed {option: true} to the constructor. How can I make that work?
Pre-declare the class with a #typedef with the optional properties and set it to the constructor as a variable of type {new () => ExampleClass}. This way it's even possible to declare this as the defined class and have code completion inside the constructor function itself. Something like the below:
/**
* Settings you can use to configure an instance of an {#link ExampleClass}
*
* #typedef {Object} ExampleOptions
* #property {true} [option] You can set this to `true` to make a property on an instance of {#link ExampleClass} exist. If you don't set it, that property won't exist
*/
/**
* #typedef {object} ExampleClass
* #prop {boolean} [property] A property that only exists based on the options given to this constructor
*/
/**
* A thing that does stuff
*
* #constructor
* #param {ExampleOptions} [param] Options you can use to configure the new instance
* #this {ExampleClass}
* #type {{new () => ExampleClass}}
*/
const ExampleClass = function (param) {
if(param.option === true) {
this.property = true;
}
}
Can you not just do
interface options {
option? : boolean;
}
/**
* A thing that does stuff
*
* #constructor
* #param {options} [param] Options you can use to configure the new instance
*/
function ExampleClass(param : options) {
if(param.option === true) {
/**
* A property that only exists based on the options given to this constructor
*
* #type {boolean}
*/
this.property = true;
}
}

js-doc / google-closure-compiler how to document passed enum object itself?

e.g.
/**
* Super my enum
* #enum {number}
*/
var MyEnum = {
ONE: 1,
TWO: 2
};
/**
* #param {what type is it?} enumObj
*/
function showEnum(enumObj) {
console.log(enumObj);
}
//show the enum definition object
showEnum(MyEnum);
how to describe parameter type as not the value/instance of MyEnum, but as the MyEnum object itself?
Use !MyEnum where the ! means "non-null".
/**
* #param {!MyEnum} enumObj
*/
function showEnum(enumObj) {
console.log(enumObj);
}
the solution i tested in WebStorm and VSCode for autocompletion is to use typeof MyEnum.
Its still invalid JSDoc, but supported for autocompletion by IDEs.
/**
* #param {typeof MyEnum} enumObj
*/
function showEnum(enumObj) {
console.log(enumObj);
}

Documenting a private constructor with JSDoc

I've got a class where individual methods may be called statically but will return a new instance of class in order to chain, for example:
var builder = ns
.setState('a', 'A')
.setState('b', 'B');
Where Builder is defined as such:
/**
* #module Builder
*/
/**
* #class Builder
*/
/**
* #private
*/
function Builder() {
this.state = {
query: {}
};
}
Builder.prototype = {
/**
* #param {string} k - The key
* #param {object} v - The value
* #return {Builder}
*/
setState: function(k, v) {
var that = (this instanceof Builder) ? this : new Builder();
that[k] = v;
return that;
}
// Other properties and methods…
}
The Builder constructor is never supposed to be called explicitly by user code and thus I'd like it not to show up in the docs. However, all of the combinations I've tried with JSDoc tags (e.g. #private, #constructs, etc.) can't seem to suppress it from the built docs.
From version 3.5.0 of jsDoc, you can use tag #hideconstructor on the class, telling jsDoc not to include the constructor into documentation.
/**
* #class Builder
*
* #hideconstructor
*/
function Builder() {
// implementation
}
You should be able to use the #ignore directive to achieve this. From the docs:
The #ignore tag indicates that a symbol in your code should never appear in the documentation. This tag takes precedence over all others.
http://usejsdoc.org/tags-ignore.html

Documenting the return of a javascript constructor with jsdoc

I have a javascript function that returns a constructor (see code sample below). How would I document this with the #returns tag of jsdoc. It doesnt seem correct to do #returns {MyConstructor} because that implies I am returning an instance of "MyConstructor" rather than the constructor itself, right?
function MyConstructor() {
var self = this;
self.myFunction = function() {
return true;
};
self.getMyFunctionResult = function() {
return self.myFunction();
};
}
/**
* #returns {?} A constructor that will be instantiated
*/
function getConstructor() {
return MyConstructor;
}
var constructor = getConstructor();
var instance = new constructor();
I do not think there is a way to use the brackets after #returns to document returning a specific instance. What goes in the brackets is interpreted as a type, always. This being said, there's a way to document that a specific instance of a type is being returned, by documenting the instance and using a link to the instance. I've shortened the code in the question to the essentials necessary to illustrate:
/**
* #class
*/
function MyConstructor() {
}
/**
* #returns {Function} A constructor that will be instantiated. Always
* returns {#link MyConstructor}.
*/
function getConstructor() {
return MyConstructor;
}
It can also be done with other things than classes:
/**
* #public
*/
var foo = 1;
/**
* #returns {number} {#link foo}.
*/
function getFoo(){
return foo;
}
As far as I know, this is as good as it gets with jsdoc 3.
Maybe little bit late, but I have problem to find proper answer for your question event today.
When I try generate JSDoc automatically on WebStorm, this is what I get:
class Test {}
/**
*
* #return {Test}
* #constructor
*/
function getTestConstructor() {
return Test;
}
Return type definition is still strange, but constructor annotation may fulfill the purpose.
You can check the types returned by your functions using:
console.log(typeof constructor, typeof instance); // function object
In the documentation it says:
/**
* Returns the sum of a and b
* #param {Number} a
* #param {Number} b
* #returns {Number} Sum of a and b
*/
function sum(a, b) {
return a + b;
}
http://usejsdoc.org/tags-returns.html
So in you example it would be:
/**
* Returns the MyConstructor class
* #returns {Function} MyConstructor class
*/
function getConstructor() {
return MyConstructor;
}
Or if you are creating an instance of an Item:
/**
* Returns an instance of the MyConstructor class
* #returns {Object} MyConstructor instance
*/
function getInstance() {
return new MyConstructor();
}

Properly type instance variables with closure compiler

I cannot get to properly type the following piece of code:
/**
* #constructor
*/
function F() {
this.a = 0;
};
/**
* #type {function(number)}
*/
F.prototype.g = function(b) {
this.a += b;
};
I get the following warning:
test.js:12: WARNING - could not determine the type of this expression
this.a += b;
^
How can I properly type this in this example?
-- EDIT --
If you want to see the warning, you need to set reportUnknownTypes to trueas explained here. I am trying to get to 100% typed code and I figured I could not reach that for that simple a program.
/** #type {function(number)} */
doesn't specify the "this" type so it is unknown. To specify it in that fashion you would want to use:
/** #type {function(this:F, number)}
Using "#param {number}" lets the compiler infer the "this" type from the fact it was declared on F's prototype.
You seem to need to use #param {number} b instead of #type {function(number)}. Typing with #param doesn't throw the warning. Doesn't make a whole lot of sense, but it works:
/**
* #param {number} b
*/
F.prototype.g = function(b) {
this.a += b;
};
--
Original answer:
I think it's because you didn't type a in your constructor. Try this:
/**
* #constructor
*/
function F() {
/**
* #type {number}
* #private
*/
this.a = 0;
};

Categories

Resources