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