Skip to content

default types for @template generics in JSDoc #29401

Closed
@Jamesernator

Description

@Jamesernator

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

changed the title [-]Template default types in JSDoc[/-] [+]@template generic default types in JSDoc[/+] on Jan 14, 2019
changed the title [-]@template generic default types in JSDoc[/-] [+]default types for @template generics in JSDoc[/+] on Jan 14, 2019
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
SamB

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 :-).

VincentGuinaudeau

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'.
rohit-gohri

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)
thw0rted

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 @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.

trusktr

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.

thw0rted

thw0rted commented on Jun 8, 2020

@thw0rted

@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.

michaelfig

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.

bhgsbatista

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
 */
jonlepage

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 :)

thw0rted

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.
jhunterkohler

jhunterkohler commented on Jul 3, 2021

@jhunterkohler

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

alexander-akait

alexander-akait commented on Aug 26, 2021

@alexander-akait

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

intrnl

intrnl commented on Sep 18, 2021

@intrnl

doesn't #45483 resolve this?

alexander-akait

alexander-akait commented on Sep 18, 2021

@alexander-akait

Yep

Jamesernator

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

        Participants

        @SamB@trusktr@michaelfig@thw0rted@weswigham

        Issue actions

          default types for @template generics in JSDoc · Issue #29401 · microsoft/TypeScript