Home > Net >  JSDoc generic typedef for TypeScript
JSDoc generic typedef for TypeScript

Time:08-05

I am using JSDoc in a JavaScript project which is checked using the TypeScript compiler.

In TypeScript, I can create a generic type like so:

type Optional<TType extends string> = { type: TType; optional?: boolean; } | { type: `${TType}?` };
type OptionalThing = Optional<'thing'>;

The resolved and flattened value of OptionalThing is now this:

type OptionalThing = 
  | { type: 'thing'; optional?: boolean; }
  | { type: 'thing?'; }

This type creates a type union where one member type has the literal and const thing value for the type field and it has an additional optional boolean field and the other member type has literal and const thing? for the type field and doesn't have the optional field.

The ? suffix on type is a short-hand syntax for marking a thing as optional in the problem I am solving so specifying optional in addition needs to be forbidden otherwise it would be possible to represent an invalid value: { type: 'thing?', optional: false }.

In other words:

/* These are allowed - they conform to the type */
const optionalThing1: OptionalThing = { type: 'thing?' };
const optionalThing2: OptionalThing = { type: 'thing', optional: true };
const optionalThing3: OptionalThing = { type: 'thing', optional: false };

// This is disallowed, `optional` cannot be specified while `type` has `?` suffix
const optionalThing4: OptionalThing = { type: 'thing?', optional: false };

// This is disallowed, `type` must be either `thing` or `thing?` const string
const optionalThing5: OptionalThing = { type: 'not literally "thing"' };

Playground (configured to TypeScript)

I am having trouble translating this to JSDoc that would have the same behavior in TypeScript. I tried this:

/** @typedef {{ type: T; optional?: boolean; } | { type: `${T}?`; }} Type<T> */
/** @template {string} T */

/** @typedef {Type<'thing'>} ThingType */
// Error: Type 'Type' is not generic.

Playground (configured to JavaScript)

How can I made a generic typedef in JSDoc such that the above TypeScript logic works the same way when TypeScript checks the JavaScript and JSDoc version?

CodePudding user response:

Your code is almost right, you just have to place in the same JSDoc comment both the @template and @typedef tags, like this:

/** 
 * @typedef {{ type: T; optional?: boolean; } | { type: `${T}?`; }} Type<T>
 * @template {string} T
 */

/**
 * @typedef {Type<'thing'>} ThingType
 */

Playground Link

In general, when you need to define a generic type you'll need to have alongside that @typedef tag also a @template tag in the same JSDoc comment.

You can't however place the ThingType typedef in the same JSDoc, otherwise it will become a generic type as well.

  • Related