A way to specify class properties using JSDoc
See original GitHub issueSearch Terms
JSDoc class properties any key @property
Suggestion
I would like to be able to extend the type of an ES6 class using JSDoc comments, such that TypeScript understands there are other properties that might be added to the object later.
Alternatively, I would like a way to indicate that a given class acts as a container that can contain any possible key.
Use Cases
I have a class that is used like a DTO. It has some properties that are guaranteed to be there, but is also used for a lot of optional properties. Take, for example:
class DTO {
constructor(id) {
/**
* @type {string}
*/
this.id = id;
}
}
TypeScript now recognizes the object type, but whenever I try to use other properties, it complains. Currently, I’m resorting to something like this as a work-around:
class DTO {
constructor(id) {
/**
* @type {string}
*/
this.id = id;
/**
* @type {Object?}
*/
this.otherProperty;
}
}
But it’s ugly and verbose, and worse, it includes actual JavaScript code, that serves no purpose other than to provide type information.
Examples
Rather, what I would like to do is something like this (that I would like to be equivalent to the snippet above):
/**
* @property {Object} [otherProperty]
*/
class DTO {
constructor(id) {
/**
* @type {string}
*/
this.id = id;
}
}
Another equivalent alternative could be (but currently doesn’t compile because of a “Duplicate identifier” error):
/**
* @typedef {Object} DTO
* @property {string} id
* @property {Object} [otherProperty]
*/
class DTO {
constructor(id) {
this.id = id;
}
}
Or, otherwise, some way to indicate this class can be extended with anything. Which means I would like a way to specify the following TypeScript using JSDoc and I want it to apply to an existing ES6 class (because I do use the class to be able to do instanceof
checks):
interface DTO {
id: string,
[key: string]: any,
}
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. new expression-level syntax)
Issue Analytics
- State:
- Created 5 years ago
- Reactions:32
- Comments:21 (7 by maintainers)
Top GitHub Comments
Here is my use case, which involves somersaults through the hoops of making good JSDocs for Proxy declarations in pure JavaScript.
The problem here is that I need the following things to work simultaneously:
@param {MyClass}
for JSDoc@param {MyClass}
for TypeScriptinstanceof MyClass
Class constructor version
Factory version using empty class
Factory version using @typedef
Why I don’t like the workarounds
@typedef
implementation above shows the kind of weirdness that happens if you need to use the functionality ofinstanceof
with a dummy class. The entire implementation works fine, but reinventing theinstanceof
wheel sort of jumps off the future code maintenance cliff. Future junior programmers are not going to understand why the functionisInstanceOfMyClass
even exists, let alone go looking for it to use it until their code blows up.@property
statements anyway so that the JSDocs come out correctly. Documenting things twice is not good.@property
since its inception. It’s not like JSDoc added some new feature and you haven’t caught up yet. This should have been one of the first pieces of JSDoc-to-TypeScript support. Plus, you already support@property
for@typedef
, so there can’t be that much extra code needed.Note that this syntax,
introduces runtime behavior that will break code because the
other
field gets defined inconstructor
with anundefined
value that can shadow anything asuper()
call may have put in place.Here’s an example of how it breaks. This is the plain JS code:
Now we try to add types with JSDoc and it breaks:
We need the equivalent of
declare other: number
in TS for plain JS / JSDoc.I understand we can refactor code to make it work, but in some cases that may be a lot more work, especially in rather large plain JS projects.