How to type a JavaScript function with JSDoc + TypeScript?

1.2k Views Asked by At

I'm trying to use tsc with plain, Vanilla JS and I'm stumped on how to declare the type a function.

It seems like it should be this simple:

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

Reduced Test Case

Ignore the fact that someone may want to refactor this into using classes or prototypes or some such - it serves well as a demonstration of the problem.

Repo: 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;

Incorrectly Typed as "any"

When I run tsc to check it gives an error about an implicit any:

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

What to do?

To me this seems like a bug in tsc... but this is JS 101 stuff, surely there must be a way to type a function?

What annotation do I need to use to declare the function's type? Or can tsc / tsserver / typescript just not handle this kind of rudimentary use of JS?

1

There are 1 best solutions below

1
On BEST ANSWER

Solution 1

You should use @callback instead of @function like so:

"use strict";

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

/**
 * @callback 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;

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

  return person;
};

module.exports = Person;

Solution 2

Or, declare a @typedef using typescript syntax like the following:

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