Typescript: 提案:定義前の使用を禁止する

作成日 2014年07月15日  ·  29コメント  ·  ソース: microsoft/TypeScript

コードが値を初期化する前に使用すると、コンパイラはエラーを発行する必要があります。

// Error, 'Derived' declaration must be after 'Base'
class Derived extends Base { }
class Base { }
Bug

最も参考になるコメント

今日はこれに噛まれたばかりで、何が起こっているのかを理解するのに少し時間がかかりました。

TypeScript v1.8.10、Webpackベースのビルド、基本クラスと派生クラスの両方が同じファイルで定義されていますが、(明らかに)順序が間違っており、コンパイルエラーや警告はなく、ソースマップが機能している場合でも、エラー呼び出しスタックが指していました非常に役に立たない場所(派生したクラスをインポートする別のクラスの終わり)。

全体の議論を通過するわけではありませんが、応急処置として、コンパイラの警告が役立つようです。 ちょうど私たちの2¢

全てのコメント29件

コンパイラエラーをスローすることは良い解決策ですが、おそらくコンパイラはクラスを正しい順序で出力できます。 それはキラー機能になります。 たとえば、コンパイラは依存関係を追跡し、それに応じてクラスを出力し、依存順序を解決できない場合にのみコンパイラエラーをスローします。

コンパイラーは依存関係を追跡し、それに応じてクラスを出力し、依存関係の順序を解決できない場合にのみコンパイラエラーをスローします。

これを新しい提案にする必要がありますか? これが、私が現在TypeScript内部モジュールではなくAMD​​モジュールを使用している理由です。 RequireJSコンパイラは、コードベース全体で指定した依存関係を使用して( require() )適切なモジュールのシリアル化順序を決定します。

#274へのリンク。 これのルールと範囲がどうなるかを概説する必要があります

extendsケースは、字句チェックの良い候補のようです。 字句的に基本クラスが派生クラスの前に来るようにする必要があります。 他に考慮すべきケースはありますか?

1つの問題は、クラス定義を並べ替えると、静的初期化子の順序がサイレントに並べ替えられる可能性があることです。 基本クラスが派生クラスの後に来る場合は、クラス定義サイトで静的初期化コードを維持するか、エラーにフラグを立てることに投票します。

複数ファイルの場合は、大規模なプロジェクト/メンテナンスの観点から(結局のところ、typescriptの表向きの目標です)、より興味深く有用だと思います。

したがって、単一ファイル出力モードでの出力順序を考慮する必要があると思います。 (複数のファイルを含むhtmlファイルを作成するためにこの順序を取得できると便利です)。

これが私が注文を確実にするだろうと思ういくつかのステートメントです:

class X extends Y {} // ensure Y is defined in prior file
module { new X(); } // ensure X is defined in prior file
class S { static z = new Z(); } // ensure Z is defined in prior file

これを、クラスだけでなく、使用前に定義されている関数や変数にも拡張できます。

PS私はプロトタイプを持っています。

実行時に必ず失敗するものに対して可能な場合にエラーを与えるためだけに、emitを並べ替えようとする意図はないと思います。

ダン、1つのファイル内での並べ替えについては同意しますが、-outを使用して複数のファイルを組み合わせると、コンパイラーが発行順序を制御できるため、選択した順序が機能することをお勧めします。

@sparecycles関数は、実行時にとにかくスコープの一番上に

再注文用; 私たちが従った哲学は、出力コードを入力コードにできるだけ近づけることです。 本質的には、ユーザーコードを通過させ、型を取り除くだけです。 この意味で、エラーはこれまでに行ったこととより一致します。

実装に関しては、最近LetとConstを使用して辞書式順序検証を追加しました。これは、一般的なチェックとして抽出して、これらのさまざまなケースに使用できます。 あなたはここでそれを見つけることができます:
https://github.com/Microsoft/TypeScript/blob/master/src/compiler/checker.ts#L329

チェックしているケースを明確に特定する必要があり、PRは間違いなく歓迎されます:)

はい、単一のtypescriptファイル内で並べ替えたくないことに同意しますが、-outファイルの場合、順序はユーザーによって指定されないため、コンパイラーが最善を尽くして動作する順序を選択してください。

関数の巻き上げは、単一ファイルの場合は気にする必要がないが、複数のファイルにコンパイルし、それらを.htmlファイルに含めるシーケンスを選択することは人間にとって簡単ではない場合の良い例です。 使用時に変数が未定義であるということは、 /// <reference>行の変更が原因で、コンパイラーによって予期しない動作が発生する可能性がある良い例です。

ただし、-outファイルの場合、順序はユーザーによって指定されません

これは実際にはそうではありません。 ここには非常に単純なルールがあります。 referenceタグによって示される順序と、コマンドラインでのファイルの順序を使用します。 どちらの場合も、ユーザーが注文を提供しています。 ユーザーが指定した順序をコンパイラーに無視させることは、危険な方法です。 コンパイラが希望する順序とは異なる順序を決定した場合はどうなりますか? それをどのようにオーバーライドしますか? 1つの注文が2つのクラスを分割し、別の注文が2つの変数を分割した場合はどうなりますか?

次に、順序を変更するべきではありませんが、コンパイラが使用する順序がおそらく間違っていることを少なくとも(オプションで)ユーザーに警告する必要がありますか?

うん。 注文するべきではありませんが、代わりにエラーが発生します。

相互再帰的なクラスのためのTypeScriptの正しいイディオムは何ですか? 実際の定義の前にdeclare class

クラスが型システムまたはインスタンスメソッドで単に相互に参照している場合、それは問題ありません。 問題となる唯一の「相互再帰的」パターンは次のとおりです。

class Alpha {
    static myFriendBeta = new Beta();   
}

class Beta {
    static myFriendAlpha = new Alpha(); 
}

これをcloduleとして書き直すことができます。

class Alpha {
}

class Beta {
    static myFriendAlpha = new Alpha();
}

module Alpha {
    export var myFriendBeta = new Beta();
}

さて、「基本クラスは派生クラスの前に字句的に定義する必要があります」以外に、この問題の一部として実装したいルールは何ですか?

内部モジュールメンバーへの前方参照を禁止します。

var x = M.fn(); // Should error
module M {
    export function fn() {}
}

列挙型メンバーへの前方参照を禁止する

var x = E.A; // Should error
enum E { A }

IMOは、通常、すべてのコードを1つのファイルで定義するわけではないため、この問題の範囲はかなり限定されています。基本クラスは、派生クラスとは別のファイルに存在する可能性が高くなります。

@sparecyclesからの以下も、この問題の解決策の一部である必要があることをお勧めします。

次に、順序を変更するべきではありませんが、コンパイラが使用する順序がおそらく間違っていることを少なくとも(オプションで)ユーザーに警告する必要がありますか?

「コンパイラが使用する順序」には、 tsconfig指定された順序を含める必要があります。

基本クラスと派生クラスが同じファイルにある場合、問題はそれほど悪くはありません。プログラムは起動時にクラッシュし、プログラマーはその1つのファイルの順序を変更し、問題は_修正_されます。

複数ファイルの場合は、複数のファイルを疑わしい順序で連結するときにコンパイラーが警告する必要がある場合です。その順序は微妙な理由で変更される可能性があるためです。

基本クラスと複数の派生クラスがすべて別々のファイルにある場合を考えてみます。 基本クラスは、その実装で派生クラスの一部を使用するため、それらを参照しますが、それでも出力の最初に配置する必要があります。 同様に、すべての派生クラスは基本クラスを参照する必要があります。

A.tsがB.tsを相互に参照し、X.tsがA.tsを含む場合、出力順序は[B、A、X]になり、B.tsを参照する場合は、相互に参照されるファイルに問題はありません。順序は[A、B、X]になります。 (ただし、実行時に機能するのはこれらの順序の1つだけです。)これにより、BまたはAのいずれかが参照された場合にコンパイルが同じように成功するため、問題が発生しやすくなります。

基本派生クラスシステムの問題に対する私の解決策:クラス階層にindex.tsを追加し、このファイルにすべての派生クラスを含め、その後に基本クラスを含めます。 これにより、出力が基本クラスを最初に配置することが保証されました。 (完全に直感に反します!)。 必要なファイルを直接参照すると、派生ファイルの後に基本クラスが生成されることがわかりました。

コンパイラの警告は非常に便利ですが、相互参照シナリオの参照の1つにemit-orderingとしてフラグを立てることができ、もう1つは宣言をプルするためだけのものであると便利です。 相互の放出順序参照はエラーになります。

(現在、マルチファイル出力を使用しているため、Visual Studio / Typescriptプロジェクトに.jsインクルードのリストを自動的に生成するためにこれを実装しています(デバッグが簡単です)。ただし、コードはインラインタスクとしてC#にあります。興味があります。共有できるかどうか尋ねます。基本的には、TarjanのCCアルゴリズムを2回実行します。)

放出順序が間違っている場合の警告と、明示的なディレクティブによる放出順序の安定化の両方が、typescriptを大規模プロジェクトで実行可能な言語にするのに大いに役立ちます...はい?

私はかなり小さなコードベース(約80 .tsファイル)でこの問題にかなり頻繁に遭遇します。 理想的には、ファイルの先頭に<reference>タグを付けたくないので、コンパイラーがすべてを処理してくれます。

私のアプリには、クラスをインスタンス化してアプリケーションを実行するファイル(コンポジションルート)が1つだけあり、拡張子を追加するファイル( Array.prototype.distinct追加など)がいくつかあり、残りはクラス/インターフェイスの定義にすぎません。

この場合、ほとんどのコードは並べ替えのための公正なゲームであり、正しく行うために手動で<reference>定義を必要とすべきではありません。 クラス定義はコンパイラーの並べ替えにとって公正なゲームであると考えており、結合された出力の先頭までシャントする必要がありますが、残りのステートメントは入力時の順序を維持できます。

コンパイラフラグ--looseSortingは可能でしょうか? かなり人気のある機能のようです。

ES6のemitclass-declarationでは、class-declarationの後に静的プロパティの割り当てを行います。 これにより、計算されたプロパティ名でクラスの静的プロパティを参照すると、use-before-definitionになります。

放出されたJS:

class C {
    [C.p] () {}  // Use before definition
    [C.p+ C.e]() {}  // Use before definition
    [D.f] () {}  // Use before definition
}
C.p = 10;
C.e = 20;

class D {
}
D.f = "hi";

このエラーについて警告するのは、2つの場合のみです。計算されたプロパティ名はそのクラスの静的プロパティを参照するか、その下に定義されている他のクラスのプロパティを参照します。

テストに含めるために、今日遊んでいた些細な例:

function f() {
    function g() {
        i = 10;
    }

    let i = 20;
    g();
}

i周りのgの使用/定義の順列を取得するのは良いことです。

ブロックスコープ内で定義された関数について考えることを忘れないでください。 これはJavaScript標準による未定義の動作であり、少なくともFirefoxとChromeは実装に同意していません。

例えば:

function f() {
    if (true) {
        g(); // iirc, g executes in Chrome, and is undefined in Firefox
        function g() {
        }
        g(); // works in both browsers
    }
}

現在https://github.com/Microsoft/TypeScript/issues/2854によって追跡されてい

今日はこれに噛まれたばかりで、何が起こっているのかを理解するのに少し時間がかかりました。

TypeScript v1.8.10、Webpackベースのビルド、基本クラスと派生クラスの両方が同じファイルで定義されていますが、(明らかに)順序が間違っており、コンパイルエラーや警告はなく、ソースマップが機能している場合でも、エラー呼び出しスタックが指していました非常に役に立たない場所(派生したクラスをインポートする別のクラスの終わり)。

全体の議論を通過するわけではありませんが、応急処置として、コンパイラの警告が役立つようです。 ちょうど私たちの2¢

TSがこの機能をすぐにサポートしないのはばかげていると思います。 それが引き起こす混乱は、標準のJSを使用する場合と同様です。 また、仮想メソッド、誰か?

@MrGuardianOPで説明されている再現が修正されました。 おそらく、あなたはあなたが抱えている問題をよりよく説明する新しい問題または既存の問題で明確にすることができますか?

(#12673)IMOがエラーになるはずの別の2つのケースは次のとおりです。

「」
クラステスト
{{
_b = this._a; //未定義、エラー/警告なし
_a = 3;

static _B = Test._A; // undefined, no error/warning
static _A = 3;

method()
{
    let a = b; // Block-scoped variable 'b' used before its declaration
    let b = 3;
}

}
「」

@Spongman別の問題でそれを記録できますか? ありがとう!

12673

このページは役に立ちましたか?
0 / 5 - 0 評価