Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@typedef {Function} generates a type that can be read, but can't be applied #50274

Open
coolaj86 opened this issue Aug 11, 2022 · 4 comments
Open
Labels
Domain: JSDoc Relates to JSDoc parsing and type generation Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Milestone

Comments

@coolaj86
Copy link

coolaj86 commented Aug 11, 2022

🔎 Search Terms

callback typedef function alias semantic jsdoc tsc tsserver

🕗 Version & Regression Information

Always, up to Version 4.7.4

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about @callback

Summary

You should be able to create a typedef of a Function and use that type for any applicable function.

For example:

/**
 * @typedef {Function} PersonGreet
 * @param {String} name - other's name
 * @returns {String} - the greeting
 */

/** @type PersonGreet */
let greet = function (name) {
    return `Hello ${name}, my name is Rob the Robot!`;
}

However, this fails:

The type of a function declaration must match the function's signature.

Parameter 'name' implicitly has an 'any' type.

Complete Example

⏯ Playground Link

https://www.typescriptlang.org/play?filetype=js#code/EQVwzgpgBGAuBOBLAxrYBuAUJg9AKj0yjygAFYBPABwgBMIAzKAbwHkAjAKwlQF8oAChHhgA9gDsiJUlXiia8SiwDKCROIDm-cQEMAthCllZ84UuZCREgOLwIEWPw12HUnNnyFiZSjXpNmADEQcVRECX5LMXFbe1gjGR14fRYoiKhRWAALYQS7WBB4cTAVNU1eN2wAGwdBYWioAF4WXixcAgSqJJS2Lh5HKCpO7r1SpHLBgDpdAzyHQuLU+ojKtPFJ5DsdWGhmhhCwiSgACioAShYiKGua2EHl8SaWtuvrhWjp-V2pmYgX188Pmo0DWsVqeHcr3uVnWzjiT32oVg4UecIcx0yOXgF2YVyh13yCygAAMABIQKpVURQAAkzExwk+Bl4ABooHoKFBflBECU6e8JEyILwAITErBQ1rYKGEorQ6JYKWYPSiWggGqTCAADyookUJWaa3QQA

💻 Code

"use strict";

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

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

let 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 (other) {
        return `Hello ${other.name}, my name is ${person.name}!`;
    };

    return person;
};

module.exports = Person;

🙁 Actual behavior

In the example you can see that the checker doesn't apply the type PersonGreet to the function being assigned to person.greet.

The type declared with /** @typedef {Function} */ is parsed, but it can't be used to to type a compliant function directly (although it is somewhat usable as an export that other modules can understand the type in some contexts).

🙂 Expected behavior

A type generated with @typedef {Function} should be able to be applied to functions.

Workaround

There's no documentation for this, but it just so happens that if you use the @callback alias some extra machinery kicks in and you can type functions as you would have expected.

/**
- * @typedef {Function} PersonGreet
+ * @callback PersonGreet
 * @param {String} name - other's name
 * @returns {String} - the greeting
 */

/** @type PersonGreet */
let greet = function (name) {
    return `Hello ${name}, my name is Rob the Robot!`;
}

The problem is that @callback is usually incorrect and confusing:

  • all functions are functions
  • not all functions are callbacks
  • in fact, most functions are not callbacks

It's also possible to use TypeScript's proprietary arrow notation, but this generates other errors:

/**
- * @typedef {Function} PersonGreet
- * @param {String} name - other's name
- * @returns {String} - the greeting
+ * @typedef {(
+ *     other: Person
+ * ) => string} PersonGreet
 */

/** @type PersonGreet */
let greet = function (name) {
    return `Hello ${name}, my name is Rob the Robot!`;
}
JSDoc '@typedef' tag should either have a type annotation or be followed by '@property' or '@member' tags.
@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Experience Enhancement Noncontroversial enhancements Domain: JSDoc Relates to JSDoc parsing and type generation labels Aug 11, 2022
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Aug 11, 2022
@coolaj86 coolaj86 changed the title typedef {Function} is not a proper alias of @callback @typedef {Function} is treated differently than it's less-semantic alias @callback Aug 13, 2022
@coolaj86 coolaj86 changed the title @typedef {Function} is treated differently than it's less-semantic alias @callback @typedef {Function} is treated differently than its less-semantic alias @callback Aug 13, 2022
@coolaj86 coolaj86 changed the title @typedef {Function} is treated differently than its less-semantic alias @callback @typedef {Function} is treated differently than its less-semantic alias @callback Aug 13, 2022
@coolaj86
Copy link
Author

When I originally went through the fill-out-form-wizard I was just following the steps. Having come back to look at this I realize that that process didn't result in clear communication about this bug (something is actually broken and requires a workaround, not simply an experience enhancement).

I've updated description and the text to better communicate the bug.

@coolaj86 coolaj86 changed the title @typedef {Function} is treated differently than its less-semantic alias @callback @typedef {Function} generates a type that can be read, but can't be applied Aug 13, 2022
@RyanCavanaugh
Copy link
Member

all functions are functions
not all functions are callbacks
in fact, most functions are not callbacks

I don't understand what this means. A callback is a pattern where a function is given a reference to another function, which is then invoked. For example [1, -2, 3].map(Math.abs) is a legal way to process an array -- what is the distinction you're trying to draw here?

@coolaj86
Copy link
Author

coolaj86 commented Aug 15, 2022

@RyanCavanaugh I don't want to start any side discussions that will detract from the bug in the issue. I'd be happy to carry on that conversation about my suggested workaround somewhere else. Perhaps here? https://gist.github.com/coolaj86/cfc802175d21c90d74fdc04fa27e23b0

The bug is that /** @typedef {Function} ... */ is not working correctly - it's not providing a @typeable type.

@coolaj86
Copy link
Author

coolaj86 commented Sep 5, 2022

In short: A callback defines that a function is used asynchronously. .map() is synchronous. It can accept an anonymous function, but trying to use a callback would result in buggy code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Domain: JSDoc Relates to JSDoc parsing and type generation Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

2 participants