Typescript: [Design Policy] Consider JSDoc feature parity with Typescript

Created on 28 Mar 2019  ·  13Comments  ·  Source: microsoft/TypeScript

Search Terms

jsdoc parity, jsdoc equivalence, jsdoc

Suggestion

The JSDoc mode of TypeScript is very useful in cases where a build step (esp for node libraries for example) isn't desired or when other constraints prevent not writing in JS. However it can be annoying when trying to implement something that can't be expressed in JSDoc mode due to requiring Typescript syntax.

As such I would like to propose that TypeScript ensures that anything that can be written inside a .ts file can be expressed (in at least some way) within a pure javascript + jsdoc file.

In order to get an idea of the current scope needed for feature parity this is a list of issues and features that break parity between the two modes (if any are missing just say and I'll add to the list):

  • [Bug?] No way to express the object type

    • Currently in JSDoc /** @type {object} */ is equivalent to /** @type {any} */, there doesn't seem to be any way to represent const x: object purely in JS + JSDoc, this seems like a bug.

  • interface
  • abstract class
  • protected/private members
  • function overloading
  • defaults for generics
  • declare syntax in it's various forms and declaration merging

    • declare global { ... }

    • declare module "moduleName"

    • declare class Foo

    • declare interface

    • declare namespace

  • namespace
  • enum

    • /** @enum */ is not quite equivalent

  • as const

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.
Meta-Issue

All 13 comments

Function overloading can be done using:

/** @type {((name: string) => Buffer) & ((name: string, encoding: string) => string))} */
const readFile = (name, encoding = null) => { … }

I discovered this purely by accident.

  • as const

30445

.d.ts files can contain many of these declarations without breaking JS builds, since they don't have to be imported.

But they don't work for the current file, only imported ones.

But they do!

// test.d.ts
interface X { a: string }

// test.js
/** @type {X} */
const x = { a: 'one' };

You just have to include the .d.ts files in your tsconfig.json.

By the way, many of these tricks I also discovered by pure accident; it would be nice if they were properly documented. The page dedicated to this has improved a lot recently, but there's still a lot of things I had to figure out by trial-and-error.

@steinuil Ok I should be more specific then. Function overloading does not work - at least the last time I checked:

// test.d.ts
declare function test(arg1: string, arg2: number, arg3: () => void): void;
declare function test(arg2: number, arg3: () => void): void;
declare function test(arg1: string, arg3: () => void): void;
declare function test(arg3: () => void): void;

// test.js
function test(arg1, arg2, arg3) {
  // ... args are `any`
}

Also nonNull! #23405.

@AlCalzone function overloads only affect usages of the function, not parameter types within a function - this is true in TS, too. You need to actually annotate parameter types on the implementation (compatible with the overloads) to get checking in the function body.

Also, in function overloads, it’d be great if the implicit arg types were unknown instead of any, but that depends on #27265 (and #30813).

@global doesnt seem to work
image

having them in the same file yields he same result

{ the error is cannot find name 'foo' }

Would e.g. #28730 fall under this umbrella? I'm trying to use JSDoc to describe existing sources that define getter/setter properties with Object.defineProperties and the only way I've found to do so is with @memberof. (For an example, see my comment on that issue.)

I'm using this hack to get support for the @private JSDoc tag currently. Would love to see this land in TS.

const REGEXP = /@private(?<suffix>[\n\r\s\w]+)\*\/(?<whitespace>[\n\r\s]+)(?<memberName>[\w]+)\b/g;

const source = `
export class Foo {
  /** Focuses the element. */
  focus(): void;
  /**
   * @param  {string} message
   * @return {Error}
   * @private
   */
  createError(message: string): Error;
}
`

source.replace(REGEXP, (...args) => {
  const [{suffix, whitespace, memberName}] = [...args].reverse();
  return `@private${suffix}*/${whitespace}private ${memberName}`
})
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dlaberge picture dlaberge  ·  3Comments

wmaurer picture wmaurer  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

fwanicka picture fwanicka  ·  3Comments