What is the best way to format a JavaScript function using JSDoc and TypeScript?

Latest Update (2023)

Recently, I presented a talk on the topic of integrating tsc with JSDoc, addressing not only this question but several related issues:

I also introduced a package to simplify the tsconfig setup:

The Original Question Posed

My current challenge involves using tsc with plain Vanilla JS and struggling with defining the type for a function.

It appears that the solution should be straightforward:

/** @type PersonGreet */
person.greet = function greet(other) {
  return `Hello ${other.name}, my name is ${person.name}!`;
};

Note: The usage of /** @type PersonGreet */ is indeed correct. The issue at hand seems to be a bug in tsc. Various valid workarounds are discussed in the answer below.

Simplified Test Scenario

While one might consider refactoring this code to utilize classes or prototypes, it effectively illustrates the problem at hand.

Repository: https://github.com/BeyondCodeBootcamp/hello-tsc

"use strict";

/**
 * @typedef {Object} Person
 * @property {String} name
 * @property {PersonGreet} greet
 */

/**
 * @typedef {Function} PersonGreet
 * @param {Person} other
 * @returns {String}
 */

let Person = {};

/**
 * Creates a person
 * @param {Object} p
 * @param {String} p.name
 * @returns {Person}
 */
Person.create = function (p) {
  let person = {};

  person.name = p.name;

  /////////////////////////////////////////////////////////////////////////////////
  //
  // error TS7006: Parameter 'other' implicitly has an 'any' type.  <======= WRONG!
  //
  /////////////////////////////////////////////////////////////////////////////////

  /** @type PersonGreet */
  person.greet = function greet(other) {
    return `Hello ${other.name}, my name is ${person.name}!`;
  };

  return person;
};

module.exports = Person;

Misidentified as "any"

Upon running tsc for verification, an error concerning implicit any types is reported:

tsc -p jsconfig.json
person.js:28:33 - error TS7006: Parameter 'other' implicitly has an 'any' type.

28   person.greet = function greet(other) {
                                   ~~~~~


Found 1 error in person.js:28

How to Proceed?

In my view, this issue seems like a flaw within tsc... However, considering this is fundamental JavaScript knowledge, there must be a way to specify a function's type, right?

What annotation should be used to declare the function's type? Or is it possible that tsc, tsserver, or typescript cannot handle such basic JavaScript concepts?

Answer №1

Best Practice

It is recommended to utilize the @callback annotation rather than @function as shown below:

"use strict";

/**
 * @typedef {Object} Person
 * @property {String} name
 * @property {PersonGreet} greet
 */

/**
 * @callback PersonGreet
 * @param {Person} other
 * @returns {String}
 */

let Person = {};

/**
 * Generates a new person
 * @param {Object} p
 * @param {String} p.name
 * @returns {Person}
 */
Person.create = function (p) {
  let person = {};

  person.name = p.name;

  /** @type {PersonGreet} */
  person.greet = function greet(other) {
    return `Hello ${other.name}, I am ${person.name}!`;
  };

  return person;
};

module.exports = Person;

Alternate Approach

Alternatively, define a @typedef utilizing typescript syntax like so:

/**
 * @typedef {(other: Person) => string} PersonGreet
 */

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Performance comparison between Ember and Angular.JS in rendering a large table

I am planning to construct a substantial table containing a plethora of data (approximately 2000 elements <td>). I intend to include functions for calculating values based on model, but without incorporating any bindings. Primarily, my goal is to s ...

Insert newly added rows' values into the database dynamically

Hello there, I'm currently working on a PHP form that needs to dynamically add a table row when the "Add" button is pressed. I'm using a for loop to save the values, but I'm running into an issue where the data is not being saved into my dat ...

Investigating Javascript memory usage in Internet Explorer 6

Our application is experiencing severe performance issues in IE6 due to its heavy reliance on JavaScript and the majority of activity taking place within a single page. In this browser, we are noticing that memory continues to accumulate without being cle ...

How to use AngularJS to parse JSON containing backslashes

Here is the JSON data stored in $scope.projectData: [{ "\"Space\"": "\"L1 (1 floor)\"", "\"Subject\"": "\"Corridor Distance\"", "\"Label\"": "\"Corridor Distance\"", "\"Color&bso ...

AngularJs: Safely cleaning and rendering HTML content

I have been attempting to dynamically display HTML inside a div, but the ng-bind-html attribute isn't showing up at all on Firefox, Chrome, and Safari. After browsing through other posts on this website, I learned that ngSanitize needs to be included ...

Developing personalized angular decorators

I've been working on creating custom decorators that can return a new instance of a class. The catch is that this class needs to be created from an injected service, and I'm struggling to access it from the decorator function. Custom Decorator ...

Using a functional wrapper component to reset the modal field in Reactstrap upon closing and reopening

In the main component that displays a list of to-do tasks, we have the ability to add or edit existing tasks. To facilitate this functionality, a separate wrapper was created. import React, { useEffect, useState } from 'react'; import { Label ...

The multi-level navigation bar is not displaying properly

I am currently facing an issue with my Mega menu. It displays two levels of menus perfectly fine, but I need to add a third level as shown in the image below. However, when I try to include the third level, it disrupts the design and causes the Grand Child ...

Change the text inside a container without losing any associated event listeners using JavaScript or jQuery

Here is the HTML code: <div id="div001"> This is ${Row.1}. <p>${Row.2} explain something else.</p> <p>${Row.3} welcome you. <span>${Hello.World}, this is a new world.</span></p> </div> My objective is ...

Angular G-map

Hey there, I'm currently working on Angular Google Maps and facing some challenges when it comes to setting the bounds and map center. You can check out my progress at https://github.com/dylanfprice/angular-gm <gm-map gm-map-id="'infoWindows ...

Invalid file location of original image

I'm experiencing an issue where I am unable to capture the input image from my device. Instead, it keeps showing me a default image that I have set and provides a fake path for the input. Can someone help me solve this problem and provide some sugge ...

I've been stuck for hours, is there anything I should include?

I'm attempting to access http://localhost:4200/Personnes/view/:2, but I encountered the following error (ERROR TypeError: Cannot read property 'nom' of undefined) "My personnnes.service.component.ts" `export class PersonnesService { baseUr ...

incorporating a timer into a JavaScript game

I am currently working on a memory card game where I want to include a stopwatch feature. I'm looking to display the time elapsed from when the user selects the first card until they win. However, I am struggling with keeping the stopwatch running smo ...

Change the runat attribute in JavaScript to execute on the server side

Is there a way to dynamically set the runat attribute using client-side JavaScript? I am facing the challenge of adding rows to a table after the page is loaded and must ensure that their cell data is accessible on the server side. While I am open to marki ...

What is the best way to save a POST request response to a database using NodeJs?

Currently, I am using the Post method with axios.js and node.js successfully. The request is being made, but unfortunately, the data is not being saved to the database. This is the section of my code where I implement the request using axios.js: teste ...

Utilize React and Django to showcase encoded video frames in your application

Having recently ventured into the world of web development, I've been facing a challenging problem that I can't seem to crack. My tech stack involves the use of React and Django. The issue at hand is with a 3rd party application that utilizes op ...

Resizable dimensions for the dark mode switch

My latest project involves creating a toggle button for switching between light and dark themes using CSS, HTML, and JavaScript: id("theme-btn").addEventListener("change", function() { if (this.checked) { qs(".box").setAttribute('style', ...

Instructions on invoking a function from one controller within another controller

Is it possible to invoke a function from one controller in another controller? I attempted the following but encountered failure: <div ng-app="MyApp"> <div ng-controller="Ctrl1"> <button ng-click="func1()">func1</button> ...

How to exit a dialog in an Angular TypeScript component with precision

Hey there, I'm attempting to close a dialog from the component by specifying the path in .angular-cli.json and calling the function. However, it seems that despite my efforts, the dialog isn't closing and the redirection isn't happening. He ...

Attempting to add an element to an array results in an endless cycle

Here is a jsfiddle example showcasing the issue, but proceed with caution as it contains an infinite loop that can cause the browser tab to slow down and eventually freeze when the console is opened: https://jsfiddle.net/evx1j6yf/1/ Array Data: days: [ ...