Skip to content

default types for @template generics in JSDoc #29401

Closed

Description

Search Terms

template default types, template default jsdoc, generic default jsdoc

Suggestion

In pure TypeScript we can have default values for generic parameters e.g.:

type LessThan<T> = (a: T, b: T) => boolean;

class PriorityQueue<Value, Priority=number> {
    _comparePriority: LessThan<Priority>;
    constructor(comparePriority: LessThan<Priority> = (a, b) => a < b) {
        this._comparePriority = comparePriority;
        // ...
    }

    add(value: Value, priority: Priority) {
        // ...
    }

    pop(): Value {
        // ... 
    }
}

const queue: PriorityQueue<Person, [number, string]>
  = new PriorityQueue(compareByAgeThenAlphabetical)

However there doesn't seem to be a way to represent this in JSDoc, TypeScript seems to just split @template on comma and take the longest identifier.

This proposal is to extend @template in JSDoc to support generic defaults (e.g. Priority=number).

Bikeshed syntax

The syntax isn't really important, but the capability would be helpful. I don't think this would cause any grammar issues but I'm not sure.

/**
  * @template Value, Priority=number
  */

Use Cases

Same as within TypeScript, just extended to JSDoc.

Example with bikeshed syntax

/**
  * @template T
  * @typedef {(a: T, b: T) => boolean} LessThan
  */

/**
  * @template Value
  * @template Priority=number
  */
class PriorityQueue {
    /** @type {LessThan<Priority>} */
    _comparePriority;

    /**
      * @param {LessThan<Priority>} [comparePriority]
      */
    constructor(comparePriority=(a, b) => a < b) {
        this._comparePriority = comparePriority;
        // ...
    }

    /**
      * @param {Value} value
      * @param {Priority} priority
      */
    add(value, priority) {
        // ...
    }

    /**
      * @returns {Value}
      */
    pop(): Value {
        // ... 
    }
}

/** @type {PriorityQueue<Person, [number, string]>} */
const queue = new PriorityQueue(compareByAgeThenAlphabetical)

Checklist

My suggestion meets these guidelines:

  • [✓] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [✓] This wouldn't change the runtime behavior of existing JavaScript code
  • [✓] This could be implemented without emitting different JS based on the types of the expressions
  • [✓] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [✓] This feature would agree with the rest of TypeScript's Design Goals.

Activity

  1. changed the title [-]Template default types in JSDoc[/-] [+]@template generic default types in JSDoc[/+] on Jan 14, 2019
  2. changed the title [-]@template generic default types in JSDoc[/-] [+]default types for @template generics in JSDoc[/+] on Jan 14, 2019
  3. added
    SuggestionAn idea for TypeScript
    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this feature
    Domain: JSDocRelates to JSDoc parsing and type generation
    on Jan 14, 2019
  4. SamB commented on Jul 16, 2019

    @SamB

    Well, I like the color of your bikeshed. You know, as long as it doesn't end up making anything explode :-).

  5. VincentGuinaudeau commented on Oct 25, 2019

    @VincentGuinaudeau

    In the case of functions, typescript could also infer the default value of a generic parameter from the default value of the parameter it is bounded to.

    Example :

    /**
     * @template T
     * @param  {string} key
     * @param  {T} [defaultValue=null]
     * @return {number[] | T}
     */
    function getAssociatedValue(key, defaultValue = null) {
        // ...
    }
    
    const test1 = getAssociatedValue("foo");
    const test2 = getAssociatedValue("bar", [42]);

    Here I would expect Typescript to infer that the type of test1 is number[] | null, and the type of test2 is number[].

    Currently (version 3.6.4), typescript return an error on the parameter defaultValue = null :

    Type 'null' is not assignable to type 'T | undefined'.
    
  6. rohit-gohri commented on Apr 8, 2020

    @rohit-gohri

    As jsdoc checking already supports the extends syntax (https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html#template), i,e, this is valid:

    /**
     * @template {any} T
     * @param {string} key
     * @param {T} [defaultValue]
     */
    function getAssociatedValue(key, defaultValue) {

    It'd be better to just extend that and add support for an = sign there:

    /**
     * @template {any = null} T
     * @param {string} key
     * @param {T} [defaultValue]
     */
    function getAssociatedValue(key, defaultValue) {

    Matches nicely to Equivalent ts:

    function getAssociatedValue<T extends any = null>(key: string, defaultValue?: T)
  7. thw0rted commented on May 29, 2020

    @thw0rted

    The OP's "bikeshedding" does not give a syntax for a type parameter that is both constrained and includes a default value. I would submit that it probably makes sense to support @template {BaseType} T=DefaultType rather than @template {BaseType=DefaultType} T. The former syntax more closely matches @param, and I don't think there are any other examples of syntax similar to the latter, as far as I can see.

    Has there been any opinion from the team about this? (I see Wesley Wigham (@weswigham) is the only contributor participating?) I just linked to this issue from tsd-jsdoc and it would be great if tsd-jsdoc and the TS team could all align on a syntax that works for everybody.

  8. trusktr commented on Jun 6, 2020

    @trusktr
    Contributor

    @template {number = null} T

    A problem with that is the = means an optional type in JSDoc syntax. I tried that in TypeScript playground, and @template {number = null} T results in T extends number | undefined.

    We must find a way to do this without breaking JSDoc syntax.

  9. thw0rted commented on Jun 8, 2020

    @thw0rted

    Joe Pea (@trusktr) that's another argument in favor of a @param-like syntax:

    /**
     * @param {number} [arg=1]
     */
    
    /**
     * @template {MyBase} T=MySubclass
     */ 

    Though, to be fair, @template is not specified in core JSDoc, it's a Closure extension, so the JSDoc compiler should already be ignoring it.

  10. michaelfig commented on Aug 10, 2020

    @michaelfig

    Is there any agreement on a way forward, to the extent that a PR would be welcome?

    This is a pretty severe blocker for me, since I express all my types in JSDoc, and want to add template parameters in a backward-compatible way (especially to take an existing type and turn it into a template without breaking callers). My only other option that I'd prefer not to, is to move the (many interdependent) declarations into a .d.ts and rewrite them as Typescript.

  11. bhgsbatista commented on Dec 17, 2020

    @bhgsbatista

    I want to chime in as well, I've been using JSDoc as a way to add compile safety to my plain JS code, and it's been a mostly pleasant experience, until I hit this limitation.

    I second the following syntax, as it preserves the current behavior and mirrors default type assignments in TypeScript:

    /**
     * @template {MyBase} T=MySubclass
     */
  12. jonlepage commented on Mar 15, 2021

    @jonlepage

    why not just keep jsdoc syntax ?
    Example:

    /**
     * @template T {keyof typeof $Plugin['Plugins']}
     * @param {T} _pluginName
     * @param {typeof $Plugin['Plugins'][T]} pluginOptions
     * 
     */

    we need a support for this :)

  13. thw0rted commented on Mar 15, 2021

    @thw0rted
    1. What you wrote isn't valid JSDoc. Both keyof and typeof are Typescript, and vanilla JSDoc would have no idea what to do with them.
    2. Your example doesn't include a default type argument, which is the subject of this issue.
  14. jhunterkohler commented on Jul 3, 2021

    @jhunterkohler

    I'm loosing my mind, the lack of this feature is making stuff impossibly hard.

  15. alexander-akait commented on Aug 26, 2021

    @alexander-akait

    Very very bad.... Need update generated types manually 😞

  16. intrnl commented on Sep 18, 2021

    @intrnl

    doesn't #45483 resolve this?

  17. alexander-akait commented on Sep 18, 2021

    @alexander-akait

    Yep

  18. Jamesernator commented on Sep 18, 2021

    @Jamesernator
    Author

    doesn't #45483 resolve this?

    Yes, I'll close this now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureDomain: JSDocRelates to JSDoc parsing and type generationSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions