I have a js file with a bunch of functions for modifying a user.
All the functions are documented with jsdoc making it easy to see what arguments to use and what the function does.
However I now want to extend every function with an optional context variable. So from this
/**
* creates a new user object
* #param {string} email
* #param {string} displayName
* #param {string} password
* #returns {User|Error}
*/
function newUser(email, displayName, password)
to this
/**
* creates a new user object
* #param {string} email
* #param {string} displayName
* #param {string} password
* #param {*} ctx
* #returns {User|Error}
*/
function newUser(email, displayName, password, ctx)
but since I have a lot of functions that would use the same ctx variable I thought that I could create a wrapper, like this
function withContext(ctx) {
/**
* #namespace
* #borrows newUser as new
*/
return {
new: (...args) => newUser(...args, ctx),
};
}
and it does work, in the sense that the right function seems to be called with the right arguments, but I can't seem to get IntelliSense to pick it up in vscode which has me thinking that I probably haven't written the jsdoc correctly.
according to vscode it says that the new function just takes an any[], which it of course does, but it is not what the documentation for newUser states, so am I doing something wrong with my documentation or is it simply not possible?
Seems like it is possible
/**
* #typedef {Object} UserService
* #property {newUser} newUser
*/
/**
* #returns {UserService}
*/
function withContext(ctx) {
return {
newUser: (...args) => newUser(...args, ctx),
};
}
that will "fix" IntelliSense and the documentation follows the function newUser that is previously defined
Related
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;
}
}
As title says, lets say that I have a class with many methods and 80% of those:
I need to establish also as a public (1:1 functionality)
They use each other
What approach is the best in terms of extending and testing in the future? Of course below examples are trivial, but lets assume that I got class with 20-30 methods inside. If it help, the project is written in ReactJS.
Approach 1:
This approach is the fastest for me.
No code repetition
I am utilizing functions inside class, also sharing outside
Some functions depends on other
class Approach1 {
names = [];
/* Establish methods for users also */
publicApi = {
check: this.check,
setValue: this.setValue
};
/**
* Public method 1
* #public
* #param {string} name
* #returns {boolean}
*/
check = name => name === "foo";
/**
* Public method 2 using internally method 1
* #public
* #param {string} name
* #returns {boolean}
*/
setValue = name => {
if (this.check(name)) names.push(name);
};
}
Approach 2:
I don't think this approach is good.
Code repetition
I am utilizing functions inside class, also sharing outside
Functions not depending on each other
class Approach2 {
names = [];
/* Establish methods for users also */
publicApi = {
check: this.check,
setValue: this.setValue
};
/**
* Public method 1
* #public
* #param {string} name
* #returns {boolean}
*/
check = name => name === "foo";
/**
* Public method 2 with own implementation
* #public
* #param {string} name
* #returns {boolean}
*/
setValue = name => {
if (name === "foo") names.push(name);
};
}
Approach 3:
This approach looks better, but I see a lot of additional code.
Additional code (I need to encapsulate almost every private method)
I am separating same functions to private and public
Private method can depends on each other
Public methods always depends on private methods
class Approach3 {
names = [];
/* Establish methods for users also */
publicApi = {
check: this.check,
setValue: this.setValue
};
/**
* Private method 1
* #private
* #param {string} name
* #returns {boolean}
*/
_check = name => name === "foo";
/**
* Private method 2
* #private
* #param {string} name
*/
_setValue = name => {
if (this._check(name)) names.push(name);
};
/**
* Public method 1 (encapsulating private method)
* #public
* #param {string} name
* #returns {boolean}
*/
check = name => _check(name);
/**
* Public method 2 (encapsulating private methods 1 and 2)
* #public
* #param {string} name
*/
setValue = name => {
if (this._check(name)) this._setValue(name);
};
}
Thank you for any tips :)
Approach 1 looks totally fine. What you need to think first of all - is public contract. Whether some public function use another public inside or not - not important. As you still need to cover with tests each piece of public API.
p.s. having 20-30 public methods inside one class - is a good time to rethink the design and split class to multiple classes/modules to comply with Single Responsibility Principle:)
I'm trying to figure out how to comment it correctly using JSDoc3.
/**
* Module ...
*
* #module Cookie
* #returns {{create, get, remove}}
*/
const cookie = (function () {
/**
* Function to create cookie.
*
* #param {String} name - The cookie name.
* #param {String} value - The cookie value.
* #param {Boolean} elongate - The flag to extend cookie lifetime.
*/
const create = (name, value, elongate) => {
...
};
/**
* Function to get cookie.
*
* #param {String} key - The cookie identificatior to get.
* #returns {*}
*/
const get = (key) => {
...
};
/**
* Function to remove cookie.
*
* #param {String} key - The cookie identificator to remove.
*/
const remove = (key) => {
...
};
return {
create: create,
get: get,
remove: remove
}
})();
I'm doing it this way, but generated document looks terrible. I can not rewrite this part of the code to the ES6 standard. Can you please advice?
After some trying ai found #memberof
You can use it like this:
#memberof module:YourModuleName
Do not know, if it is correct, but after that I saw that method is showed in my module doc. page.
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);
}
If I define an object like this:
/**
* My Cool Object
* #constructor
*/
function MyCoolObject() {
/**
* Cool Method, private
* #param {!string} parameter
*/
function localMethod(parameter) {
// do stuff
}
// Export the method
this.exportedMethod = localMethod;
}
I'd like to know, if at all possible, how to tell JSDOC to use the annotation for localMethod in exportedMethod, or how can I annotate exportedMethod, because if I do:
// Export the method
/**
* Cool Method
* #param {!string} parameter
*/
this.exportedMethod = localMethod;
JSDOC assumes it's a field rather than a method, then only uses the description, ignoring the #param part.
I would reduce it to:
/**
* My Cool Object
* #constructor
*/
function MyCoolObject() {
/**
* Cool Method, private
* #param {!string} parameter
*/
this.exportedMethod = function (parameter) {
// do stuff
};
}
You can do var localMethod = this.exportedMethod right after if you want a local reference to the method. In the off chance that you've over-simplified your example and you need to first assign to localMethod before assigning to this.exportedMethod you could do this:
/**
* My Cool Object
* #constructor
*/
function MyCoolObject() {
function localMethod(parameter) {
// do stuff
}
/**
* Cool Method, private
* #param {!string} parameter
* #function
*/
// Export the method
this.exportedMethod = localMethod;
}
The #function declaration tells jsdoc that it is dealing with a function.