microsoft / microsoft/TypeScript
JSDoc tag for getting around "Object literals are open-ended"
まだ誰も着手していません。
- 主要言語
- Go
- スター
- 111k
- フォーク
- 14.3k
- 平均マージ
- 2日 4時間
- マージ済み PR(30日)
- 132
説明
🔍 Search Terms
label:"Domain: JSDoc" open-ended
label:"Domain: JSDoc" literal
label:checkJs open-ended
label:checkJs literal
✅ Viability Checklist
- 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, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ 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.
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
handbook の「Object literals are open-ended」セクションとリンクされている Exact Types issue (#12936) から始め、次に例で説明されている checked-JavaScript の JSDoc の動作を調査します。ネストされたオブジェクトについて合意された範囲を含め、固定形状の推論されたオブジェクトリテラルを対象とする、文書化された JSDoc の opt-in が提供され、示されている追加プロパティへの代入が拒否されれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- javascript, typescript
- 領域
- compilers
- issue の種類
- 機能追加
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 活発さ
- 静か
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 45/100