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

default types for @template generics in JSDoc #29401

Closed
Jamesernator opened this issue Jan 14, 2019 · 15 comments
Closed

default types for @template generics in JSDoc #29401

Jamesernator opened this issue Jan 14, 2019 · 15 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Domain: JSDoc Relates to JSDoc parsing and type generation Suggestion An idea for TypeScript

Comments

@Jamesernator
Copy link

Jamesernator commented Jan 14, 2019

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.
@Jamesernator Jamesernator changed the title Template default types in JSDoc @template generic default types in JSDoc Jan 14, 2019
@Jamesernator Jamesernator changed the title @template generic default types in JSDoc default types for @template generics in JSDoc Jan 14, 2019
@weswigham weswigham added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Domain: JSDoc Relates to JSDoc parsing and type generation labels Jan 14, 2019
@SamB
Copy link

SamB commented Jul 16, 2019

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

@VincentGuinaudeau
Copy link

VincentGuinaudeau commented Oct 25, 2019

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
Copy link

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
Copy link

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
Copy link
Contributor

trusktr commented Jun 6, 2020

@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
Copy link

thw0rted commented Jun 8, 2020

@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
Copy link

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
Copy link

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
Copy link

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
Copy link

  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
Copy link

jhunterkohler commented Jul 3, 2021

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

@alexander-akait
Copy link

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

@intrnl
Copy link

intrnl commented Sep 18, 2021

doesn't #45483 resolve this?

@alexander-akait
Copy link

Yep

@Jamesernator
Copy link
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
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Domain: JSDoc Relates to JSDoc parsing and type generation Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests