π Search Terms
label:"Domain: JSDoc" open-ended
label:"Domain: JSDoc" literal
label:checkJs open-ended
label:checkJs literal
β
Viability Checklist
β Suggestion
According to the documentation, TypeScript treats inferred object literals in checked .js files as open-ended by default. This is good and makes sense, but is annoying for the times when it isn't the desired functionality.
I'd like for a new tag to be introduced. I'll be using @closed as an example, but I'm fine with whatever wording, really. The new tag would be used on an object definition in order to opt out of this open-ended behavior for that specific object literal declaration.
Ideally, this would be done recursively through the object's definition (i.e. I shouldn't have to define @closed for each sub-object), but I understand if that's too broad-reaching. I'd be fine with having that limitation.
This is obviously very similar to Exact Types (#12936), but it concerns general exact object types and structural compatibility, whereas this request is limited to a JSDoc opt-in for inferred object literals in checked JavaScript. I therefore believe this warrants its own feature request.
π Motivating Example
I use JSDoc quite extensively in my hobby work. I often run into issues like this:
class CustomFooBarElement extends HTMLElement {
_elements = {
/** @type {HTMLSpanElement|null} */
foo: null,
};
constructor() {
super();
this._elements.bar = document.createElement("span");
// ^ currently doesn't report an error
}
}
Now, you can easily work around this by adding a full object typing to the _elements object:
class CustomFooBarElement extends HTMLElement {
/** @type {{foo: HTMLSpanElement|null}} */
_elements = {
foo: null,
};
which would cause the .bar access to fail. However, maintaining such a declaration can quickly grow cumbersome when the _elements object grows in size and complexity:
class CustomFooBarElement extends HTMLElement {
/**
* @type {{
* foo: HTMLSpanElement | null,
* buttons: {
* primary: {
* a: HTMLButtonElement | null,
* b: HTMLButtonElement | null,
* },
* },
* images: {
* a: HTMLImageElement | null,
* b: HTMLImageElement | null,
* },
* }}
*/
_elements = {
foo: null,
buttons: {
primary: {
a: null,
b: null,
},
},
images: {
a: null,
b: null,
},
};
A silly example, but I think you get the point. Not to mention the fact that you've completely decoupled the type definition from the definition of the variable itself.
Now, if I instead could do something like this:
class CustomFooBarElement extends HTMLElement {
/** @closed */
_elements = {
/** @type {HTMLSpanElement|null} */
foo: null,
buttons: {
primary: {
/** @type {HTMLButtonElement|null} */
a: null,
/** @type {HTMLButtonElement|null} */
b: null,
},
},
images: {
/** @type {HTMLImageElement|null} */
a: null,
/** @type {HTMLImageElement|null} */
b: null,
},
};
It's much easier to handle, and would lead to the desired result:
this._elements.bar = document.createElement("span");
// ^^^ error: Property 'bar' does not exist
this._elements.buttons.primary.c = document.createElement("button");
// ^ error: Property 'c' does not exist
π» Use Cases
- What do you want to use this for?
Typing fixed-shape objects. See the examples above.
- What shortcomings exist with current approaches?
The object-declaration-level @type requires essentially repeating the entire structure of the object. It also decouples the type of the actual leaf property from its definition.
- What workarounds are you using in the meantime?
I started with the object-declaration-level @type, but at this point I just use the leaf-level @type and hope I don't run into any spelling mistakes or such.
π Search Terms
label:"Domain: JSDoc" open-ended
label:"Domain: JSDoc" literal
label:checkJs open-ended
label:checkJs literal
β Viability Checklist
β Suggestion
According to the documentation, TypeScript treats inferred object literals in checked
.jsfiles as open-ended by default. This is good and makes sense, but is annoying for the times when it isn't the desired functionality.I'd like for a new tag to be introduced. I'll be using
@closedas an example, but I'm fine with whatever wording, really. The new tag would be used on an object definition in order to opt out of this open-ended behavior for that specific object literal declaration.Ideally, this would be done recursively through the object's definition (i.e. I shouldn't have to define
@closedfor each sub-object), but I understand if that's too broad-reaching. I'd be fine with having that limitation.This is obviously very similar to Exact Types (#12936), but it concerns general exact object types and structural compatibility, whereas this request is limited to a JSDoc opt-in for inferred object literals in checked JavaScript. I therefore believe this warrants its own feature request.
π Motivating Example
I use JSDoc quite extensively in my hobby work. I often run into issues like this:
Now, you can easily work around this by adding a full object typing to the
_elementsobject:which would cause the
.baraccess to fail. However, maintaining such a declaration can quickly grow cumbersome when the_elementsobject grows in size and complexity:A silly example, but I think you get the point. Not to mention the fact that you've completely decoupled the type definition from the definition of the variable itself.
Now, if I instead could do something like this:
It's much easier to handle, and would lead to the desired result:
π» Use Cases
Typing fixed-shape objects. See the examples above.
The object-declaration-level
@typerequires essentially repeating the entire structure of the object. It also decouples the type of the actual leaf property from its definition.I started with the object-declaration-level
@type, but at this point I just use the leaf-level@typeand hope I don't run into any spelling mistakes or such.