microsoft / microsoft/TypeScript

Clarify class property initializer behavior

オープン
#37,640 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Docs
主要言語
Go
スター
111k
フォーク
14.4k
平均マージ
1日 19時間
マージ済み PR(30日)
117

説明

The problem

Briefly, if a class property isn't initialized with the declaration, it will be initialized as undefined, no matter how you initialize it in constructor. This will lead to v8 performance issue because of property type transition in many cases.

So I think it's very important to mention the behavior difference in documentation.

Documentation difference

In Class field declarations proposal from TC39, it's very clearly mentioned that "Fields without initializers are set to undefined."

Thus in TS handbook, there is nowhere mentioned how the field declaration without initializer will be handled. Actually the documentation lead us to believe it's the same result to initialize a property in declaration or in constructor. E.G., in the Classes default example, it says:

In the last line we construct an instance of the Greeter class using new. This calls into the constructor we defined earlier, creating a new object with the Greeter shape, and running the constructor to initialize it.

But actually it's like the TC39 proposal, if you don't assign a default value in declaration, the property will be initialized as undefined in constructor (before your actual initialize code).

Take a look at a simple example

class Test {
    x: number;
    constructor(x: number) {
        this.x = x;
    }
}

With tsc, this will be generated as the following js

"use strict";
var Test = /** @class */ (function () {
    function Test(x) {
        Object.defineProperty(this, "x", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.x = x;
    }
    return Test;
}());

As you can see, property this.x is firstly defined with void 0 (undefined) and then assigned with x parameter.

Why should we care

In the previous example, there is a type transition for the property x which is sadly not a good idea in v8. I assume it cause the Test instances to be polymorphic or it cause the Test instances generate a new hidden class. Anyway, the property access performance drops significantly after the construction.

Test case:
index.js.zip

From the simple test case with a loop of Vec3 calculations, we can see the performance difference

  1. Initialize in declaration(Vec3_104): 4-5ms
  2. Initialize in constructor(Vec3_110): 28-32ms

From more complexe use case in our engine's particle system simulation, the fps drops from 60fps to 30fps.

Conclusion

I'd like to suggest a very clear description of different initializer behaviors in TypeScript documentation, so that developers know that they really need to initialize in declaration.

If possible, we'd like TSC to improve the logic:

If developers are using constructor to initialize properties, then use that value for defineProperty directly, or use a type compatible value for defineProperty.

We have also submit an issue to babel which have similar behavior.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

issue にリンクされている TypeScript Handbook Classes ページから始め、クラスの初期化に関するその説明を TC39 class fields proposal および出力された JavaScript の例と比較してください。宣言とコンストラクター初期化の違いを、undefined による初期化の動作と、ここで説明されているパフォーマンス上の懸念を含めて文書化できれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, typescript
領域
documentation, performance
issue の種類
ドキュメント
難易度
2/5
見積もり時間
1〜3時間
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
38/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。