(@RyanCavanuaghによる更新)
「更新」、「今すぐ追加してください」などを求める前に、このコメントを参照してください。スレッドの長さをある程度合理的に保つために、ディスカッションに意味のある追加を行わないコメントは削除されます。
(注:これはIssue#1524の複製ではありません。ここでの提案は、C ++オーバーライド指定子の方針に沿ったものであり、typescriptの方がはるかに理にかなっています)
オーバーライドキーワードは、typescriptで非常に役立ちます。 これは、スーパークラスメソッドをオーバーライドするメソッドのオプションのキーワードであり、C ++のオーバーライド指定子と同様に、「_このメソッドの名前+署名は常にスーパークラスメソッドの名前+署名と一致する必要があります_」という意図を示します。 。 これは、他の方法では簡単に見逃される可能性のある、より大きなコードベースのあらゆる問題をキャッチします。
再びC++と同様に、_オーバーライドされたメソッドからoverrideキーワードを省略してもエラーにはなりません_。 この場合、コンパイラは現在とまったく同じように動作し、overrideキーワードに関連付けられた余分なコンパイル時のチェックをスキップします。 これにより、派生クラスのオーバーライドが基本クラスのシグネチャと正確に一致しない、より複雑な型指定されていないjavascriptシナリオが可能になります。
class Animal {
move(meters:number):void {
}
}
class Snake extends Animal {
override move(meters:number):void {
}
}
// Add an additional param to move, unaware that the intent was
// to override a specific signature in the base class
class Snake extends Animal {
override move(meters:number, height:number):void {
}
}
// COMPILE ERROR: Snake super does not define move(meters:number,height:number):void
// Rename the function in the base class, unaware that a derived class
// existed that was overriding the same method and hence it needs renaming as well
class Animal {
megamove(meters:number):void {
}
}
// COMPILE ERROR: Snake super does not define move(meters:number):void
// Require the function to now return a bool, unaware that a derived class
// existed that was still using void, and hence it needs updating
class Animal {
move(meters:number):bool {
}
}
// COMPILE ERROR: Snake super does not define move(meters:number):void
追加のコンパイル時検証に加えて、overrideキーワードは、typescriptインテリセンスが利用可能なスーパーメソッドを簡単に表示および選択するためのメカニズムを提供します。その目的は、派生クラスでそれらの1つを具体的にオーバーライドすることです。 現在、これは非常に扱いにくく、スーパークラスチェーンを参照し、オーバーライドするメソッドを見つけて、それを派生クラスにコピーして貼り付け、署名が一致することを保証します。
クラス宣言内:
確かに良い提案です。
ただし、次の例についてはどうでしょうか。これらは、私にとって有効なオーバーライドです。
class Snake extends Animal {
override move(meters:number, height=-1):void {
}
}
class A {...}
class Animal {
setA(a: A): void {...}
getA(): A {...}
}
class B extends A {...}
class Snake extends Animal {
override setA(a: B): void {...}
override getA(): B {...}
}
さらに、コンパイラフラグを追加して、overrideキーワードを強制的に存在させる(または警告として報告する)ようにします。
その理由は、継承されたクラスがすでに実装されている(ただし、オーバーライドではない)基本クラスのメソッドの名前を変更するときにキャッチするためです。
ああいい例。 一般的に言って、overrideキーワードを使用すると、厳密な型付きクラス階層を維持することが目的であるため、署名の_exact_マッチングを強制することが期待されます。 だからあなたの例に対処するために:
class C extends A {...}
var animal : Animal = new Snake();
animal.setA(new C());
// This will have undefined run-time behavior, as C will be interpreted as type B in Snake.setA
したがって、例(2.)は、実際には、overrideキーワードがコンパイル時に微妙なエッジケースをキャッチする方法の優れたデモです。 :)
また、両方の例が、必要となる可能性のある特定の制御/高度なJavaScriptシナリオで有効である可能性があることを再度強調します...この場合、ユーザーはoverrideキーワードを省略することを選択できます。
これは便利です。 現在、スーパーメソッドへのダミー参照を含めることでこれを回避しています。
class Snake extends Animal {
move(meters:number, height?:number):void {
super.move; // override fix
}
}
ただし、これは2番目のケース(スーパーメソッドの名前が変更されること)を防ぐだけです。 シグニチャを変更しても、コンパイルエラーは発生しません。 さらに、これは明らかにハックです。
また、派生クラスメソッドのシグネチャのデフォルトパラメータとオプションパラメータがコンパイルエラーを引き起こすとは思わない。 それは正しいかもしれませんが、JavaScriptの固有の柔軟性に反します。
@rwyborn
同じ動作を期待していないようです。
このoverrideキーワードを使用して同じ署名を確保しますが、私はそれを準備オプションとしてより多く使用します(したがって、コンパイラオプションを追加してその使用を強制するという私の要求)。
実際、私が本当に期待するのは、TSが無効なオーバーライドメソッドを検出することです(オーバーライドを使用しなくても)。
通常:
class Snake extends Animal {
move(meters:number, height:number):void {}
}
これは実際にはAnimal.move()(JSの動作)のオーバーライドであるため、エラーが発生するはずですが、互換性がありません(高さはオプションではないため、Animalの「参照」から呼び出された場合は未定義になります)。
実際、overrideを使用すると、このメソッドが実際に基本クラスに存在することを(コンパイラーによって)確認するだけです(したがって、準拠した署名を使用しますが、overrideキーワードではなく、前のポイントが原因です)。
@stephanedr 、シングルユーザーとして話すと、私はクラス階層内で厳密な型指定を強制するのが好きなので、コンパイラーは常に署名を確認する必要があることに実際に同意します(javascriptがそうでない場合でも!!)。
ただし、overrideキーワードを使用してこの動作をオプションにすることを提案する場合、最終的にjavascriptは型指定されないため、デフォルトで厳密な署名一致を適用すると、一部のjavascriptデザインパターンがTypescriptで表現できなくなることを覚えておいてください。
@rwyborn C ++の実装についてお話しいただき、ありがとうございます。これは、オプションで、ここに到達する前に機能するはずだと思っていたからです。 ただし、overrideキーワードの使用を強制するコンパイラフラグは、私の本ではうまく機能しません。
このキーワードを使用すると、開発者の不器用な入力でコンパイル時エラーが発生する可能性があります。これが、現在の形式でのオーバーライドについて最も心配していることです。
class Base {
protected commitState() : void {
}
}
class Implementation extends Base {
override protected comitState() : void { /// error - 'comitState' doesn't exist on base type
}
}
現在(1.4の時点で)上記のImplementation
クラスは新しいメソッドを宣言するだけであり、開発者はコードが機能していないことに気付くまで賢明ではありません。
提案レビューで議論されました。
ここでは、ユースケースを確実に理解しています。 問題は、この段階で言語に追加すると、削除するよりも混乱が増えることです。 5つのメソッドを持つクラス(そのうち3つはoverride
とマークされています)は、他の2つがオーバーライドされていないことを意味しません。 その存在を正当化するために、モディファイアは実際にはそれよりもきれいに世界を分割する必要があります。
泣き言を許してください、しかし正直なところ、あなたの議論はデフォルトですべてが公開されている言語のpublic
キーワードに適用されますが、実際にはかなり世界的に分かれており、 abstract
やオプションのoverride
キーワードは、開発者がより安全に感じ、ミスを減らし、時間を無駄にするのを助けるだけです。
オーバーライドは、言語のタイプミスに非常に敏感な数少ない側面の1つです。これは、オーバーライドするメソッド名の入力ミスは、コンパイル時の明らかな問題ではないためです。 override
の利点は明らかです。これにより、オーバーライドする意図を述べることができます。基本メソッドが存在しない場合は、コンパイル時エラーが発生します。 すべてが型システムを歓迎します。 なぜ誰もこれを望まないのでしょうか?
私は@hdachevに100%同意します。これも、 @ RyanCavanaughによって参照される小さな不整合は、コンパイル時チェックをメソッドオーバーライドにもたらすというキーワードの利点によって簡単に考慮されます。 C ++は、typescriptで提案されているのとまったく同じ方法で、オプションのオーバーライドキーワードを正常に使用することを再度指摘します。
複雑なOOツリーを含む大規模なコードベースでオーバーライドチェックがどの程度の違いを生むかを十分に強調することはできません。
最後に、オプションのキーワードの不整合が本当に懸念される場合は、C#アプローチを使用できます。つまり、「new」または「override」キーワードのいずれかを必須に使用します。
class Dervied extends Base {
new FuncA(newParam) {} // "new" says that I am implementing a new version of FuncA() with a different signature to the base class version
override FuncB() {} // "override" says that I am implementing exactly the same signature as the base class version
FuncC() {} // If FuncC exists in the base class then this is a compile error. I must either use the override keyword (I am matching the signature) or the new keyword (I am changing the signature)
}
アクセス修飾子のないプロパティはパブリックであることがわかっているため、これはpublic
に類似していません。 override
のないメソッドは、オーバーライドされないことが_知られていない_。
これは、デコレータ(TS1.5で提供される)を使用して実行時にチェックされるソリューションであり、オーバーヘッドがほとんどなく、適切なエラーメッセージが生成されます。
/* Put this in a helper library somewhere */
function override(container, key, other1) {
var baseType = Object.getPrototypeOf(container);
if(typeof baseType[key] !== 'function') {
throw new Error('Method ' + key + ' of ' + container.constructor.name + ' does not override any base class method');
}
}
/* User code */
class Base {
public baseMethod() {
console.log('Base says hello');
}
}
class Derived extends Base {
// Works
<strong i="9">@override</strong>
public baseMethod() {
console.log('Derived says hello');
}
// Causes exception
<strong i="10">@override</strong>
public notAnOverride() {
console.log('hello world');
}
}
このコードを実行すると、エラーが発生します。
エラー:DerivedのメソッドnotAnOverrideは、基本クラスのメソッドをオーバーライドしません
このコードはクラスの初期化時に実行されるため、問題のメソッドに固有の単体テストも必要ありません。 コードが読み込まれるとすぐにエラーが発生します。 また、本番環境のデプロイメントのチェックを行わない「高速」バージョンのoverride
にサブスクライブすることもできます。
@RyanCavanaughつまり、Typescript 1.6を使用していますが、デコレータはまだ実験的な機能であり、オーバーライドを機能させるためのハックとして大規模な本番コードベースにデプロイしたいものではありません。
さらに別の角度から試すために、一般的な型付き言語はすべて「override」キーワードをサポートしています。 いくつか例を挙げると、Swift、ActionScript、C#、C ++、F#です。 これらの言語はすべて、オーバーライドについてこのスレッドで表現したマイナーな問題を共有していますが、オーバーライドの利点がこれらのマイナーな問題をはるかに上回っていることを理解している大規模なグループが存在することは明らかです。
あなたの異議は純粋に費用便益に基づいていますか? 私が実際に先に進んでこれをPRに実装するとしたら、それは受け入れられますか?
それは単なる費用便益の問題ではありません。 Ryanが説明したように、問題は、メソッドをオーバーライドとしてマークしても、別のメソッドがオーバーライドではないことを意味しないということです。 それが理にかなっている唯一の方法は、すべてのオーバーライドをoverride
キーワードでマークする必要がある場合です(これは、義務付けられている場合、重大な変更になります)。
@DanielRosenwasser上で概説したように、C ++ではoverrideキーワードはオプションですが(Typescriptで提案されているとおり)、誰もが問題なく使用でき、大規模なコードベースで非常に役立ちます。 さらに、Typescriptでは、javascript関数のオーバーロードのために、オプションであることが実際には非常に理にかなっています。
class Base {
method(param: number): void { }
}
class DerivedA extends Base {
// I want to *exactly* implement method with the same signature
override method(param: number): void {}
}
class DerivedB extends Base {
// I want to implement method, but support an extended signature
method(param: number, extraParam: any): void {}
}
「別のメソッドがオーバーライドではないことを意味するものではありません」という引数全体については、「プライベート」とまったく同じです。 privateキーワードを使用せずに、コードベース全体を記述できます。 そのコードベースの一部の変数はプライベートにのみアクセスされ、すべてがコンパイルされて正常に動作します。 ただし、「プライベート」は、コンパイラに「いいえ、誰かがこれにアクセスしようとするとコンパイルエラーが発生します」と伝えるために使用できる追加のシンタックスシュガーです。 同様に、「オーバーロード」は、コンパイラに「これを基本クラスの宣言と完全に一致させたい。コンパイルエラーが発生しない場合」と伝えるための追加のシンタックスシュガーです。
あなたは何を知っている、私はあなたたちが「オーバーライド」の文字通りの解釈に固執していると思います。 実際にマークアップしているのは「exactly_match_signature_of_superclass_method」ですが、それはそれほど読みやすくはありません:)
class DerivedA extends Base {
exactly_match_signature_of_superclass_method method(param: number): void {}
}
私もoverrideキーワードを使用できるようにしたいのですが、overrideのマークが付けられたメソッドが基本クラスに存在しないか、別のシグネチャを持っている場合、コンパイラにエラーを生成させます。 読みやすさとリファクタリングに役立ちます
+1、ツールもはるかに良くなります。 私のユースケースはreactを使用しています。 ComponentLifecycle
メソッドを使用するたびに定義を確認する必要があります。
`` `C#
インターフェイスComponentLifecycle
{{
componentWillMount?():void;
componentDidMount?():void;
componentWillReceiveProps?(nextProps:P、nextContext:any):void;
shouldComponentUpdate?(nextProps:P、nextState:S、nextContext:any):ブール値;
componentWillUpdate?(nextProps:P、nextState:S、nextContext:any):void;
componentDidUpdate?(prevProps:P、prevState:S、prevContext:any):void;
componentWillUnmount?():void;
}
With override, or other equivalent solution,you'll get a nice auto-completion.
One problem however is that I will need to override interface methods...
``` C#
export default class MyControlextends React.Component<{},[}> {
override /*I want intellisense here*/ componentWillUpdate(nextProps, nextState, nextContext): void {
}
}
@olmobrutallユースケースは、言語に新しいキーワードを追加するのではなく、「インターフェースの実装」などのリファクタリングを提供したり、より良い補完を提供したりする言語サービスによって、より適切に解決されるようです。
気を散らさないようにしましょう:)言語サービス機能は、大規模なコードベースでインターフェイス階層を維持するためのほんの一部にすぎません。 はるかに大きなメリットは、階層のどこかにあるクラスが準拠していない場合に、実際にはコンパイル時エラーが発生することです。 これが、C ++がオプションのオーバーライドキーワードを追加した理由です(重大な変更ではありません)。 これが、Typescriptが同じことを行う必要がある理由です。
C ++オーバーライドに関するMicrosoftのドキュメントは、物事をうまくまとめています。https://msdn.microsoft.com/en-us/library/jj678987.aspx
オーバーライドを使用して、コードでの不注意な継承動作を防ぎます。 次の例は、オーバーライドを使用せずに、派生クラスのメンバー関数の動作が意図されていなかった可能性がある場所を示しています。 コンパイラは、このコードに対してエラーを発行しません。
..。
オーバーライドを使用すると、コンパイラーは、新しいメンバー関数をサイレントに作成する代わりに、エラーを生成します。
はるかに大きなメリットは、階層のどこかにあるクラスが準拠していない場合に、実際にはコンパイル時エラーが発生することです。
同意する必要があります。 私たちのチームで発生する落とし穴の1つは、実際には少し間違ったクラスを入力したり、間違ったクラスを拡張したりしたときに、メソッドをオーバーライドしたと考える人々です。
多くのプロジェクトで作業する場合のベースライブラリでのインターフェイスの変更は、TypeScriptのようなアジェンダを持つ言語での変更よりも困難です。
すばらしいものがたくさんありますが、このような奇妙な点があり、クラスレベルのconstプロパティはありません。
@RyanCavanaughは正しいですが、多くの言語がそうであるように、キーワードがないと、オーバーライドを記述した後に言語サービスがトリガーされる可能性があります。
インターフェイスの実装については、インターフェイスのほとんどのメソッドはオプションであり、パッケージ全体ではなく、必要ないくつかのメソッドのみをオーバーライドすることを目的としていることに注意してください。 チェックボックスを使用してダイアログを開くことはできますが、それでも...
私の現在の問題点はメソッドの名前を見つけることですが、将来的には、誰かがベースメソッドの名前を変更したり変更したりした場合に、コンパイル時エラーで通知を受け取ることができます。
オーバーライドを記述しないときに警告を追加するだけで、不整合を解決できませんでしたか? typescriptは、時間の終わりに悪い決定を保存するのではなく、小さな合理的な重大な変更を追加して正しいことをしていると思います。
また、要約はすでにそこにあります、彼らは素晴らしいカップルを作るでしょう:)
「オーバーライド」指定子の必要性も感じました。 中規模から大規模のプロジェクトでは、この機能が不可欠になります。敬意を表して、Typescriptチームがこの提案を拒否する決定を再検討することを願っています。
興味のある人のために、デコレータを使用してoverrideキーワードと同じ機能を提供するカスタムtslintルールを作成しました。これは、上記のRyanの提案と似ていますが、コンパイル時にチェックされます。 間もなくオープンソーシングを開始します。利用可能になり次第、投稿します。
「override」キーワードの必要性も強く感じました。
私の場合、基本クラスのメソッド名の一部を変更し、オーバーライドするメソッドの名前の一部の名前を変更するのを忘れました。 もちろん、これはいくつかのバグにつながります。
しかし、そのような機能があれば、これらのクラスメソッドを簡単に見つけることができます。
@RyanCavanaughが述べたように、このキーワードが単なるオプションのキーワードである場合、この機能は混乱を招きます。 では、この機能を有効にするためにtscで何かフラグを立ててみませんか?
この機能をもう一度考え直してください。
私にとって、overrideキーワードが役立つ場合は、C#の場合と同様に、強制する必要があります。 基本メソッドのシグネチャをオーバーライドするC#のメソッドを指定する場合は、オーバーライドまたは新規のいずれかにラベルを付ける必要があります。
C ++は、オプションのキーワードが多すぎるため、_consistency_が不可能になるため、一部の場所でC#に比べて煩わしく、劣っています。 たとえば、仮想メソッドをオーバーロードする場合、オーバーロードされたメソッドに仮想のマークを付けるかどうかを指定できます。どちらの方法でも仮想メソッドになります。 後者の方がコードを読む他の開発者を支援するので私は後者を好みますが、コンパイラーにそれを強制的に挿入させることはできません。つまり、コードベースには間違いなく、実際にあるべき場所に仮想キーワードがありません。 overrideキーワードも同様にオプションです。 私の意見では、どちらもがらくたです。 ここで見落とされているのは、コードがドキュメントとして機能し、「それを取るか、それを残す」アプローチではなく、キーワードの必要性を強制することによって保守性を向上させることができるということです。 C++の「throw」キーワードも同様です。
TypeScriptで上記の目的を達成するには、コンパイラはこの厳格な動作を「有効にする」かどうかを示すフラグが必要です。
ベースとオーバーライドの関数シグネチャに関する限り、それらは同一である必要があります。 ただし、コンパイル時のチェックを実施するために、戻り型の仕様に共分散を許可することが望ましいでしょう。
AS3からTSに参加するので、もちろん、ここでもoverride
キーワードに投票します。 特定のコードベースを初めて使用する開発者にとって、 override
を確認することは、(子)クラスで何が起こっているかについての大きな手がかりになります。 そのようなキーワードは非常に付加価値があると思います。 私はそれを必須にすることを選択しますが、それがどのように重大な変更になるかを理解できるので、オプションである必要があります。 オプションのoverride
とデフォルトのpublic
キーワードの違いに敏感ですが、あいまいさによる問題は実際には見られません。
すべての+1erについて、上記のデコレータソリューションについて十分ではないことについて話していただけますか?
私には、それは言語自体の特性ではなく、人工的な構成のように感じます。 それが設計によるものだと思います。それがそれであるからです。 これは、開発者(とにかく私)に、それが一時的なものであり、ベストプラクティスではないというメッセージを送信すると思います。
明らかに、すべての言語には独自のパラダイムがあり、TypeScriptを初めて使用する場合は、パラダイムシフトが遅くなります。 ただし、 override
は、いくつかの理由から、私にとってベストプラクティスのように感じます。 強く型付けされたkoolaidを完全に飲み込んだため、TypeScriptに切り替えています。コスト(キーストロークと学習曲線の観点から)は、エラーのないコードとコード理解の両方の利点よりもはるかに重要であると考えています。 override
はそのパズルの非常に重要な部分であり、オーバーライドするメソッドの役割に関するいくつかの非常に重要な情報を伝達します。
私にとっては、IDEの利便性についてではなく、適切にサポートされていれば間違いなく素晴らしいことであり、この言語を構築するための原則であると私が信じていることを実現することについてです。
@RyanCavanaugh私が見るいくつかの問題:
ただし、コンパイラはすでに引数リストと戻り型をチェックしています。 また、 override
がファーストクラスのキーワードとして存在したとしても、署名の同一性に関する新しいルールは適用されないと思います。
また、オーバーライドがファーストクラスのキーワードとして存在したとしても、署名の同一性に関する新しいルールを適用することはないと思います。
@RyanCavanaughでは、キーワードの意図について別のページにいる可能性があると思います。 重要なのは、署名の同一性を強制したい場合です。 これは、インターフェイスメソッドのコントラクトを定義する基本クラス上に深く複雑な階層があり、階層内のすべてのクラスがそれらのシグネチャと正確に一致する必要があるデザインパターン用です。 すべての派生クラスのこれらのメソッドにoverrideキーワードを追加することにより、署名が基本クラスに設定されたコントラクトから逸脱する場合にアラートが送信されます。
私が言い続けているように、これはいくつかの難解なユースケースではありません。 大規模なコードベース(たとえば、数百または数千のクラス)で作業する場合、これは_毎日_発生します。つまり、誰かが基本クラスの署名を変更する(コントラクトを変更する)必要があり、コンパイラーに警告を発するようにします。階層全体で一致しなくなったケース。
コンパイラはすでに不正なオーバーライドについて警告します。 の問題
現在の実装は、オーバーライドを宣言する際の意図の欠如です
方法。
前に述べたデコレータは、言語に反しているようです
達成しようとしています。 実行時間のコストが発生することはありません
コンパイル時に処理できるものですが、コストはわずかです。
うまくいかないことをできるだけ多くキャッチしたいのですが、
コードを実行して調べます。
デコレータを使用して、カスタムtslintをカスタム化することができます。
ルールですが、ツールエコシステムとコミュニティにとってはより良いでしょう
公式キーワードを持つ言語。
オプションであることは1つのアプローチだと思いますが、一部のC++コンパイラと同様です。
その使用を強制する設定可能なフラグがあります(例:suggest-override、
inconsistent-missing-override)。 これは避けるための最良のアプローチのように思われます
重大な変更があり、追加されている他の新機能と一致しているようです
最近では、null許容型やデコレータなど。
2016年3月23日水曜日21:31、 RowanWybornnotifications @github.comは次のように書いています。
そして、オーバーライドがファーストクラスのキーワードとして存在したとしても、私たちは
署名の同一性に関する新しいルールは適用されません。@RyanCavanaugh https://github.com/RyanCavanaughそれなら、あなたはそうかもしれないと思います
キーワードの意図について別のページにいる。 の要点
これは、署名の同一性を強制したい場合に使用します。 これ
深く複雑な階層が存在するデザインパターン用です
インターフェイスメソッドのコントラクトを定義する基本クラス、およびすべて
階層内のクラスは、これらのシグネチャと_正確に_一致する必要があります。 追加することにより
すべての派生クラスのこれらのメソッドのoverrideキーワード、あなたは
署名が設定された契約から逸脱した場合に警告
基本クラスで。私が言い続けているように、これはいくつかの難解なユースケースではありません。 に取り組んでいるとき
大規模なコードベース(たとえば、数百または数千のクラス)これは_everyです
day_occurrence、つまり誰かがベースの署名を変更する必要がある
クラス(コントラクトを変更する)、およびコンパイラーに警告するようにします
階層全体で一致しなくなったケース。—
このスレッドにサブスクライブしているため、これを受け取っています。
このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -200551774
@kungfusheep fwiwコンパイラは、特定のクラスの不正なオーバーライドのみをキャッチします。これは、宣言されたパラメータ間に直接衝突がある場合です。 パラメータの追加または削除をキャッチしません。また、リターンタイプの変更をキャッチしません。 これらの追加のチェックは、overrideキーワードがオンにするものです。
オーバーライドする関数にパラメーターを_追加_すると、コンパイラーは正しく警告します。
ただし、パラメータの削除は_完全に有効_です_:
class BaseEventHandler {
handleEvent(e: EventArgs, timestamp: number) { }
}
class DerivedEventHandler extends BaseEventHandler {
handleEvent(e: EventArgs) {
// I don't need timestamp, it's OK
}
}
返品タイプの変更も_完全に有効_です:
class Base {
specialClone(): Base { ... }
}
class Derived extends Base {
specialClone(): Derived { ... }
}
@RyanCavanaughはい、厳密な言語の観点からは有効ですが、これが上記の「契約」という用語を使い始めた理由です。 基本クラスが特定の署名をレイアウトする場合、オーバーライドを使用すると、派生クラスがその署名に厳密に従うようにしたいことを示しています。 基本クラスが追加のパラメーターを追加したり、戻り値のタイプを変更したりする場合、元々作成されたコントラクトが何らかの方法で変更されたため、コードベース内の一致しなくなったすべてのポイントを知りたいです。
それぞれが基本メソッドの独自のわずかに異なる順列を持つ大きなクラス階層を持つという考えは(言語の観点から有効であっても)悪夢を引き起こし、typescriptが登場する前のJavaScriptの古き良き時代に私を連れ戻します:)
オプション性がキーワードの主な問題である場合、基本クラスのメソッドがabstract
として定義されているときに、オプション性を必須にしないのはなぜですか? このように、パターンを厳密に適用したい場合は、単純に抽象基本クラスを追加できます。 コードの破損を防ぐために、コンパイラスイッチでチェックを無効にすることができます。
私たちは今、2つの異なる期待について話しているようです。 オーバーライドの欠落/不正なオーバーライドの状況があり、次に明示的な署名の状況があります。 前者がこのキーワードに期待する絶対的な最小値であることに同意できますか?
これは、インターフェイスなど、現在明示的なメソッドシグネチャを適用する方法が他にもあるためですが、現在、オーバーライドする意図を明示的に示す方法はありません。
オーバーライドされたメソッドの明示的な署名を強制する方法はありませんが、署名の変更が少なくとも「安全」であるとコンパイラが強制することを考えると、その問題の解決策については別の会話があるようです。
うん、同意した。 私が選択しなければならなかった場合、オーバーライドの欠落/不正なオーバーライドの状況は、解決すべきより重要な問題です。
私はパーティーに少し遅れています...Typescriptの全体的なポイントは、実行時ではなくコンパイル時にルールを適用することだと思います。そうしないと、すべてプレーンなJavascriptを使用することになります。 また、ハック/クラッジを使用して、非常に多くの言語で標準的なことを行うのは少し奇妙です。
Typescriptにoverride
キーワードが必要ですか? 私は確かにそう信じています。 それは必須ですか? 互換性の理由から、その動作はコンパイラー引数で指定できると思います。 正確な署名を強制する必要がありますか? これは別の議論になるはずですが、今のところ問題はありません。
これを閉じる元の理由は、オーバーライドされたすべてのメソッドにoverride
指定子が必要であるという重大な変更を導入できないためと思われます。これにより、 override
でマークされていないメソッドも事実はオーバーライドされます。
コンパイラオプションを使用して、またはクラスにoverride
とマークされた_少なくとも1つの_メソッドがある場合は、オーバーライドされるすべてのメソッドをそのようにマークする必要があります。そうでない場合はエラーになりますか?
空中にいる間にこれを再開する価値はありますか?
2016年4月8日金曜日14:38、 PeterPalotasnotifications @github.comは次のように書いています。
これを閉じる本来の理由は、紹介できないためと思われます
オーバーライドされたすべてのメソッドがオーバーライドを必要とする重大な変更
指定子。メソッドがマークされていないため、混乱します。
「オーバーライド」は、実際にはオーバーライドになる可能性もあります。コンパイラオプションを使用して、および/または
クラスでオーバーライドとマークされた少なくとも「1つの」メソッド、次に
オーバーライドはそのようにマークする必要があります、そうでない場合はエラーですか?—
あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -207434898
再開してください! コンパイラオプションに満足しています。
はい-再開してください
これを再開してください!
ES7では、同じものを持つ複数のメソッドをオーバーライド/オーバーロードする必要はありません
名前?
2016年4月8日午前10時56分、「AramTaieb」 [email protected]は次のように書いています。
はい、これを再開してください!
—
このスレッドにサブスクライブしているため、これを受け取っています。
このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -207466464
+1-私にとって、これは私が日々のTypeScript開発で持っている型安全性の最大のギャップです。
「componentDidMount」のようなReactライフサイクルメソッドを実装するたびに、関連するReactドキュメントページを検索し、メソッド名をコピーして貼り付けて、タイプミスがないことを確認します。 これを行うのは、タイプミスによるバグが間接的で微妙であり、追跡にはるかに長い時間がかかる可能性があるのに対し、20秒かかるためです。
5つのメソッドを持つクラス(そのうち3つはオーバーライドとマークされています)は、他の2つがオーバーライドされていないことを意味しません。 その存在を正当化するために、モディファイアは実際にはそれよりもきれいに世界を分割する必要があります。
これが主な懸念事項である場合は、キーワードcheck_override
を呼び出して、チェックをオーバーライドすることを選択していることを明確にします。暗黙的に、他のメソッドは_オーバーライドされない_ではなく_チェックされない_です。
implements.
を使用するのはどうですか? このようなものであること:
class MyComponent extends React.Component<MyComponentProps, void>{
implements.componentWillMount(){
//do my stuff
}
}
この構文にはいくつかの利点があります。
implements.
を書き込んだ後、IDEにはオートコンプリートポップアップを表示する優れた機会があります。class MyComponent<MyComponentProps, MyComponentState> {
implements.state = {/*auto-completion for MyComponentState here*/};
implements.componentWillMount(){
//do my stuff
}
}
注:代わりに、 base.
を使用することもできます。これは、ソーターでより直感的ですが、おそらくより混乱し(定義または呼び出し?)、その意味はインターフェースの実装とそれほど互換性がありません。 例:
class MyComponent<MyComponentProps, MyComponentState> {
base.state = {/*auto-completion for MyComponentState here*/};
base.componentWillMount(){ //DEFINING
//do my stuff
base.componentWillMount(); //CALLING
//do other stuff
}
}
implements
がすべてのユースケースを十分にカバーしているとは思いません
前述しましたが、少しあいまいです。 それはもう与えません
override
もできなかったIDEへの情報なので、
他の多くの言語が使用してきた用語に固執することは理にかなっています
同じことを達成します。
2016年4月13日水曜日19:06、 Olmonotifications @github.comは次のように書いています。
道具の使用はどうですか。 このようなものであること:
クラスMyComponentはReact.Componentを拡張します
{{
implements.componentWillMount(){
//自分のことをする
}
}この構文にはいくつかの利点があります。
- 既存のキーワードを使用します。
- 筆記具を書いた後。 IDEには優れた機会があります
オートコンプリートメソッド。- キーワードは、抽象のチェックを強制するために使用できることを明確にします
基本クラスのメソッドだけでなく、実装されたインターフェイス。- 構文はあいまいなので、次のようなフィールドでも使用できます。
これ:クラスMyComponent
{{
implements.state ={/_MyComponentStateのオートコンプリートhere_/};implements.componentWillMount(){ //do my stuff }
}
—
あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -209571753
オーバーライドの問題は、同じキーワードを使用すると、同じ期待値を持つことです。
abstract
の場合は、必須である必要があります。virtual
キーワードを見逃します。TSチームは言語にそれほど多くのOOの荷物を追加したくないと思います、そして私は良い考えだと思います。
implements.
を使用することで、主な利点を得る軽量の構文が得られます。新しいキーワードを発明したり、概念の数を増やしたりすることなく、名前のオートコンプリートとコンパイル時にチェックされます。
また、クラスとインターフェイス、およびメソッド(プロトタイプ内)または直接フィールドで機能するという利点もあります。
override
必須にする方法はスレッドですでに説明されており、ソリューションはコンパイラーによって実装された他の機能と同じです。
virtual
キーワードは、言語のコンテキストでは実際には意味がありません。また、C++などの言語をこれまで使用したことがない人にとって直感的な名前でもありません。 おそらく、このタイプのガードを提供するためのより良い解決策は、必要に応じて、 final
キーワードです。
「手荷物」を作成する可能性のある言語機能を急いで追加するべきではないことに同意しますが、 override
は、他の多くの言語が必要と見なしている継承システムの正当な穴を塞ぎます。 その機能は、新しいキーワードを使用して最もよく実現されます。確かに、代替アプローチが基本的な構文の変更を提案する場合です。
継承されたフィールドを設定するのと、新しいフィールドを再定義するのはどうですか? React state
は良い例です。
または、オプションのインターフェイスメソッドを実装しますか?
フィールドが再宣言されている場合、オーバーライドがフィールドで使用される可能性があります
サブクラス本体で。 オプションのインターフェースメソッドはオーバーライドされないため、オーバーライドされます
ここで話している範囲外です。
2016年4月14日木曜日11:58、 Olmonotifications @github.comは次のように書いています。
継承されたフィールドを設定するのと、新しいフィールドを再定義するのはどうですか? 反応状態
良い例です。または、オプションのインターフェイスメソッドを実装しますか?
—
あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -209879217
オプションのインターフェースメソッドはオーバーライドされないため、ここで説明している範囲外です。
オプションのインターフェイスメソッドは、私が知る限り、TypeScriptにかなり固有の概念であり、「オーバーライド」のTypeScript実装を適用する必要があると思います。
componentDidMountのようなReactライフサイクルメソッドの場合、これらはスーパークラスに実装されていないオプションのインターフェイスメソッドです。
componentDidMountのようなReactライフサイクルメソッドの場合、これらはスーパークラスに実装されていないオプションのインターフェイスメソッドです。
正確に、それらは何もオーバーライドしません。 override
キーワードがここで提供することを意図していることについて、私たちは混乱していると思います。なぜなら、より良いコードヒント/インテリセンスを取得する方法を探しているだけなら、追加せずに達成できる他の方法があるからです。言語への新しいキーワード。
みんな、敬意を表して、集中してください。 特に問題が特定の要求で始まったので、代替キーワードについて議論することは逆効果だと思います。
override
が必要であることに同意し、Typescriptチームに追加するように依頼するか、リクエストを削除して問題を解決することができますか?
問題が何であるかという文脈でリクエストを提出することは常に有用だと思います。問題を解決するには常にさまざまな方法があります。 オーバーライドの問題点は、必須かどうかということでした(C ++では必須ではないという点もあります)。 多くの人が言及している問題は、オーバーライド可能な関数(スーパークラスに実装されている場合とされていない場合があります)の名前を正しく取得することです-Reactのライフサイクル関数が主な問題です。
オーバーライドが機能しない場合は、この問題を閉じて、この問題に関するより一般的なポイントを開く必要があります。 ここでの一般的な問題は、スーパークラスとのインターフェースコントラクトが型指定されておらず、チェックされていないことです。これは開発者をつまずかせます。TSまたはツールが役立つとしたら素晴らしいでしょう。
@armandnクロージャの欠如や提案の良さは、リクエストを拒否し続けているものではないと思いますが、C#とTSのセマンティクスの違いは次のとおりです。
C#ベースメソッドのオーバーライド:
override
キーワードが必要ですC#インターフェイスの実装:
したがって、C#での動作は、クラスまたはインターフェイスについて質問するかどうかによって大きく異なりますが、いずれの場合でも、主な3つの利点があります。
セマンティクスがわずかに異なるため、TSにはすでに1)がありますが、残念ながら2)と3)が欠落しています。 私の意見では、 override
が拒否された理由は、同様の構文が同様の動作を想定しているためです。これは、次の理由で望ましくありません。
5つのメソッドを持つクラス(そのうち3つはオーバーライドとマークされています)は、他の2つがオーバーライドされていないことを意味しません。
_objectliterals_を書くときはすでに1)2)と3)がありますが、他のクラスを拡張したりインターフェースを実装したりするクラスメンバーを書くときはありません。
これを念頭に置いて、私たちは皆、このセマンティクスに同意できると思います。
check
またはsuper.
、またはMyInterface.
)abstract/virtual
のチェックはありませんが、基本クラス/実装されたインターフェイスにメンバーが存在するかどうかをチェックします。 (メリット2)さらに、ソリューションを完全にするために、この2つが必要だと思います*。
この2つのポイントは、Reactコンポーネント実装の非常に現実的なユースケースで役立ちます。
`` `C#
クラスMyComponent
implements.state={/ここでMyComponentStateのオートコンプリート/};
implements.componentWillMount(){
//do my stuff
}
}
`` `
@RyanCavanaughのアノテーションベースのソリューションでは、次の理由で十分ではありません。
@override
を書き込んだ後、オートコンプリートリストを書き込んだ後にQuickFixを追加する方法はありません。セマンティクスを考えると、それは正しい構文を選択することだけです。 ここにいくつかの選択肢があります:
override componentWillMount()
:直感的ですが誤解を招く恐れがあります。check componentWillMount()
:明示的ですが、キーワードを消費します。super componentWillMount()
:implements componentWillMount()
:super.componentWillMount()
:implements.componentWillMount()
:this.componentWillMount()
:ReactComponent.componentWillMount()
:意見?
@olmobrutall良い要約。 ポイントのカップル:
1つのキーワードオプション(C#から取得)は新しいものになります-新しいスロットを示します:
class MyComp extends React.Component<IProps,IState> {
...
new componentWillMount() { ... }
componentWillMount() { ...} // would compile, maybe unless strict mode is enabled
new componentwillmount() { ... } <-- error
私の他のポイントは、上記を使用することの必須の性質の問題です。 親スーパークラスと派生クラスの間のコントラクトとして、上記の構文が有効になるインターフェイスポイントを指定することは理にかなっています。 これらは実際にはスーパークラスの内部拡張ポイントであるため、次のようになります。
class Component<P,S> {
extendable componentWillMount() {...}
}
同じことがインターフェースにも当てはまります。
ありがとう :)
this
と書くだけではどうですか?
class MyComponent<MyComponentProps, MyComponentState> {
this.state = {/*auto-completion for MyComponentState here*/};
this.componentWillMount(){
//do my stuff
}
}
extendable
については、TS 1.6にはすでにabstract
があり、仮想を追加すると、スプリットワールドの問題が再び発生する可能性がありますか?
そうです、抽象について考えましたが、スーパークラスに実装があるかもしれないので、あまり意味がありません。 仮想の場合と同様に、非仮想メンバーは仮想ではないことを意味するため、誤解を招く可能性があります。
this.
機能します、私はあなたが(長い形式として)持つことさえできると思います:
this.componentWillMount = () => { }
これに関する唯一の問題は、基本クラスのすべてのメンバーではなく、指定された拡張ポイントのみにスコープを設定する必要があることです。
何が起こっている...
TypeScriptは、C#のjavascriptバージョンではありません。 したがって、その理由は、提案された機能がC#のセマンティクスと異なるためではありません。 ライアンが述べ、ダニエルRが明らかにした閉鎖の理由は、
Ryanが説明したように、問題は、メソッドをオーバーライドとしてマークしても、別のメソッドがオーバーライドではないことを意味しないということです。 それが理にかなっている唯一の方法は、すべてのオーバーライドをオーバーライドキーワードでマークする必要がある場合です(これは、必須の場合、重大な変更になります)。
それでも、オートコンプリートに関する問題はまだ解決していません。 オートコンプリートでは、改善された提案を提供するために言語の新しいキーワードは必要ありません。
このスレッドが存在する理由は、関数が不正にオーバーライドされた場合、またはメソッドがオーバーライドとして宣言されたが、基本クラスの関数を実際にはオーバーライドしなかった場合にコンパイラエラーを取得するためでした。 これは、typescriptが提供しなければならない言語機能のほとんどをサポートする、多くの言語にわたる単純で明確に定義された概念です。 すべての世界の問題を解決する必要はありません。オーバーライドするには、overrideキーワードである必要があります。
それ以来、より多くの人々が元の提案に関心を示しているので、問題が提起されたトピックに固執し、新しいアイデアのために新しい問題を提起しましょう。
TypeScriptは、C#のjavascriptバージョンではありません。
参照言語としてC#と比較しています。これは、皆さんが想定している動作であると想定しているためです。
オートコンプリートでは、改善された提案を提供するために言語の新しいキーワードは必要ありません。
それでは、どのようにトリガーすることを提案しますか? クラスコンテキストでランダムな名前を書き込むときにオートコンプリートコンボボックスを表示すると、新しいフィールドまたはメソッドを宣言するだけで非常に煩わしくなります。
このスレッドが存在する理由は、関数が不正にオーバーライドされた場合、またはメソッドがオーバーライドとして宣言されたが、基本クラスの関数を実際にはオーバーライドしなかった場合にコンパイラエラーを取得するためでした。
私はこのユースケースをBenefit2に絶対に含めました。
すべての世界の問題を解決する必要はありません。オーバーライドするには、overrideキーワードである必要があります。
したがって、あなたの提案は、1ステップ戻って同様の/関連する問題を調べるのではなく、_一度に1ステップ_を修正することです。 これはスクラムボードには良い考えかもしれませんが、言語の設計には適していません。
キーワードの追加について保守的である理由は、言語から機能を削除できないためです。
完成度の欠如によるC#のいくつかの設計ミス:
var
は変数型に対してのみ機能し、自動ジェネリックパラメーターや戻り型に対しては機能しません。 たぶんauto
はC ++のようにより良いキーワードになるでしょうか?Reactを1秒間使用してみると、画像の反対側が表示されます。
基本クラスにすでに実装されているメソッドをオーバーライドすることと、インターフェースメソッドを実装することは、_2つのまったく異なることです_。 そうです、提案されているのは、Swiss-Armyキーワードを考え出すのではなく、専用のキーワードを使用してこれらのシナリオの1つを修正することです。
初めてコードを読む開発者にとって、3つの異なることのいずれかを意味するキーワードは何が良いのでしょうか。 これはあいまいで混乱を招きます。特に、言語で他の(まったく関係のない!)ことをすでに実行しているthis
のようなキーワードを使用する場合は、これ以上一般的ではなく、ほとんど役に立ちません。
主な関心事がオートコンプリートである場合、編集者は「入力時に」基本クラスと実装されたインターフェースからメソッドを提案できる十分な情報を持っています。
基本クラスにすでに実装されているメソッドをオーバーライドすることと、インターフェイスメソッドを実装することは、まったく異なる2つのことです。
一般的なケースではそうですが、インターフェイスメソッドの実装については話していません。 _親クラスがinterfaceを実装する_オプションのinterfaceメソッドについて話しています。 この場合、1)インターフェイスでメソッドをundefined
として実装でき、2)親クラスに未定義の実装があり、3)子クラスがメソッド実装で未定義の実装をオーバーライドしていると言えます。
@olmobrutall言語の設計と、それがスクラムボードではないことについてのあなたのコメントは、少し自己奉仕的だと思います。 1年足らずの間にTSの更新が約4回見られました。
言語設計があなたが示唆するように十分に考慮されていれば、オーバーライドがどのように機能するかを正確に説明する言語仕様文書がすでに存在し、この会話すらできないかもしれません。
TSはすでに優れており、代わりに標準のJSを使用する必要があるので、TSの開発者/設計者を軽蔑してこのコメントをすることはありません。
はい、TSはC#ではなく、C++でもありません。 しかし、多くの言語がここで説明した目的を満たすためにオーバーライドキーワードを選択しているため、完全に異質な構文を提案することは逆効果のようです。
主な問題は、重大な変更を導入したくないようです。 簡単な答えは、コンパイラフラグ、話の終わりです。 私のような一部の人々にとって、オプションのオーバーライドキーワードは役に立たない。 他の人にとっては、コードを段階的に装飾したいと考えています。 コンパイラフラグはジレンマを解決します。
署名の違いは別の会話です。 JSは同じ名前の複数のメソッドをサポートできないため、新しいキーワードは不要のように見えます(TSが署名から派生したマングル名をC ++で作成しない限り、これはほとんどありません)。
1年足らずの間にTSの更新が約4回見られました。
私はあなたが速くそして速く繰り返すことができないという意味ではありません。 ES6とTSが急速に進化していることを誰よりも嬉しく思います。 私が言いたいのは、言語が行き詰まるのを避けるために、あなたは未来を予測しようとしなければならないということです。
override
キーワードの使用に同意できます。 適切な引数を使用すると、フィールドとインターフェイスを範囲外に保つこともできますが、「_集中し続けて、他の言語があまり考えずにこの特定の問題を解決する」という引数に同意することはできません。
しかし、多くの言語がここで説明した目的を満たすためにオーバーライドキーワードを選択しているため、完全に異質な構文を提案することは逆効果のようです。
この言語には、プロトタイプの継承やオプションのメソッド(抽象でも仮想でもないメソッド、実行時に_存在しない_可能性がある)はありません。これは、コミットメントを行う前に話し合う(そしておそらく破棄する)必要がある関連する問題です。
別の言い方をすれば、あなたが提案しているように私たちが行い、あまり考えずにオーバーライドを実装するとしましょう。 次に、私またはTSXを使用している他の人が、 override
がReactコンポーネントで機能しない理由に関する問題を追加します。 あなたの計画は何ですか?
一般的なケースではそうですが、インターフェイスメソッドの実装については話していません。 親クラスがインターフェイスを実装するオプションのインターフェイスメソッドについて話しています。
インターフェースがどこに設定されているかは関係ありません。実際、それらは同じものではなく、プログラムの_意図_が明確でないため、キーワードを共有するべきではありません。
たとえば、基本クラスのインターフェイスコンプライアンスのために実装されたメソッドをオーバーライドすることができます。 これら2つの異なるものに対してすべての卵を1つのキーワードに入れるとしたら、これがその関数の最初の宣言なのか、それとも基本クラスで以前に定義された関数のオーバーライドなのかを誰かが知るにはどうすればよいでしょうか。 あなたはそうしません、そして基本クラスのさらなる検査なしでは知ることはできません-それはサードパーティの.d.ts
ファイルにあるかもしれません、そしてそれ故にそれを見つけるのは絶対的な悪夢になります継承チェーンでは、関数は元々実装されていました。
別の言い方をすれば、あなたが提案しているように私たちが行い、あまり考えずにオーバーライドを実装するとしましょう。 次に、私またはTSXを使用している他の人が、オーバーライドがReactコンポーネントで機能しない理由に関する問題を追加します。 あなたの計画は何ですか?
なぜこれでReactを修正する必要があるのですか? Reactがこれが解決しようとしているものとは異なる問題を抱えている場合、 override
がそれを修正する必要がある理由を私は一生理解できませんか? 別の問題を開いて、インターフェイスの実装について何かを行うことを提案しましたか?
私はこれに十分な考えが入れられていないことに同意しません。 私たちは、それが実装されていると私が考えることができる他のすべての言語で成功している、試行錯誤されたテクニックを提案しています。
事実、それらは同じものではなく、プログラムの意図が明確でないため、キーワードを共有するべきではありません。
そうじゃない? BaseClass
のこの2つの代替定義を見てください
class BaseClass {
abstract myMethod();
}
interface ISomeInterface {
myMethod?();
}
class BaseClass extends ISomeInterface {
}
そして、あなたのコードであなたはします:
`` `C#
クラスConcreteClass{
myMethod()をオーバーライドする{
//何かをする
}
}
You think it should work in just one case and not in the other? The effect is going to be 100% identical in Javascript (creating a new method in ConcreteClass prototype), from the external interface and from the tooling perspective.
Even more, maybe you want to capture `this` inside of the method, implementing it with a lambda (useful for React event handling). In this case you'll write something like this:
``` C#
class ConcreteClass {
override myMethod = () => {
// Do stuff
}
}
メソッドが抽象であるか、インターフェースからのものである場合、動作は再び同じになります。クラスにフィールドを追加し、ラムダを実装します。 ただし、値を割り当てるだけなので、フィールドのoverride
には少し奇妙に見えます。
super.
を使用してそれを見てみましょう(今のところ私のお気に入りの構文ですが、私は代替案を受け入れています)。
`` `C#
クラスConcreteClass{
super.myMethod(){//プロトタイプのメソッド
//何かをする
}
super.myMethod = () => { //method in lambda
// Do stuff
}
}
`` `
これで、概念は概念的に単純になりました。私のスーパークラスは、メソッドまたはフィールドがあり、ConcreteClassがプロトタイプでそれを定義/割り当て/読み取り/呼び出すことができると言っています。
なぜこれでReactを修正する必要があるのですか?
ただ反応するだけでなく、角度を見てください:
もちろん、ほとんどのインターフェースは実装されることを意図しておらず、すべてのクラスがオーバーライドされることもありませんが、1つ明らかなことがあります。Typescriptでは、インターフェースはクラスよりも重要です。
別の問題を開いて、インターフェイスの実装について何かを行うことを提案しましたか?
どのように呼ぶべきですか? インターフェイスとフィールドにoverride
?
私たちが考えることができる他のすべての言語で成功している、試行錯誤されたテクニックを提案しています。
あなたが考えている言語はかなり異なります。 それらは静的VTableに基づく継承を持っています。
Typescriptでは、クラスは単なるインターフェイス+メソッドの自動プロトタイプ継承です。 そして、メソッドは内部に関数を持つ単なるフィールドです。
override
機能をTSに適合させるには、この2つの基本的な違いを考慮する必要があります。
@kungfusheepは、私の問題の解決策について考える努力をしてください。 別のキーワードを追加して第2段階で実装する場合は問題ありませんが、少し時間を取って、どのようにすべきかを想像してください。
私がすでに言ったことを言う別の方法を考えることはできません。 それらは同じではありません。 それらは似ていますが、同じではありません。 TS開発者REの1人によるこのコメントを参照してください:読み取り専用キーワード-https: //github.com/Microsoft/TypeScript/pull/6532#issuecomment-179563753-これは私が言っていることを強化します。
私は一般的な考え方に同意しますが、実際に見てみましょう。
class MyComponent extends React.Component<{ prop : number }, { value: string; }> {
//assign a field defined in the base class without re-defining it (you want type-checking)
assign state = { value : number};
//optional method defined in an interface implemented by the base class
implement componentDidMount(){
}
//abstract method defined in the base class
override render(){
}
}
これはVBまたはCobolのように見えますね。
少なくとも、それは理にかなっているようです。
この例を考えてみましょう。override(または1つだけ)のキーワードしかありませんでした。
interface IDo {
do?() : void;
}
class Component implements IDo {
protected commitState() : void {
/// do something
}
override public do() : void {
/// base implements 'do' in this case
}
}
次に、今書いたものを使用して独自のコンポーネントを実装しましょう。
class MyComponent extends Component {
override protected commitState(){
/// do our own thing here
super.commitState();
}
override do() : void {
/// this is ambiguous. Am I implementing this from an interface or overriding a base method? I have no way of knowing.
}
}
知る方法の1つは、 super
のタイプです。
override do() : void {
super.do(); // this compiles, if it was an interface then super wouldn't support `do`
}
まさに! これは、デザインが間違っていることを意味します。 スキミングコードに関連する調査は必要ありません。それを読んだときに意味があるはずです。
これはあいまいです。 これをインターフェースから実装していますか、それとも基本メソッドをオーバーライドしていますか? 私には知る方法がありません。
実際の違いは何ですか? オブジェクトには名前のスペースが1つだけあり、 do
フィールドに配置できるラムダは1つだけです。 とにかく、インターフェースを明示的に実装する方法はありません。 実装は次のようになります。
MyComponent.prototype.do = function (){
//your stuff
}
あなたが書いたものとは無関係に。
出力が何であるかは関係ありません。 基本クラスの一部の機能を意図的または意図せずにオーバーライドする可能性がありますが、あいまいなキーワードには意図がありません。
2つのキーワードを使用することで、どのようなエラーまたは予期しない動作が解決されますか?
さあ、仲間に来てください。 あなたは明らかに賢い人です。 「基本クラスの一部の機能を意図せずにオーバーライドする」と言ったばかりです。 これから、発生した可能性のある予期しない動作を推測することはできませんか?
明確にするために、私はこの問題を2つのキーワードの提案に変えることを提案していません。 この問題はoverrideキーワードに関するものです。それ以外の場合は、新しい提案とセマンティクスに関する独自の議論が必要になります。
明確にするために、私はこの問題を2つのキーワードの提案に変えることを提案していません。 この問題はoverrideキーワードに関するものです。それ以外の場合は、新しい提案とセマンティクスに関する独自の議論が必要になります。
議論すべき問題の数や、アイデアの出所は重要ではありません。 あなたは2つの非常に関連性のある考えを分割することを提案し、一貫した構文を考慮しません。
1、2、または3つのキーワードが必要かどうかの議論はこのスレッドに属し、終了していません(...しかし繰り返しになります)。 次に、別のスレッドで構文について説明することができます(セマンティクスはとにかく同一になるため:P)。
私の例では:
class MyComponent extends React.Component<{ prop : number }, { value: string; }> {
//assign a field defined in the base class without re-defining it (you want type-checking)
assign state = { value : number};
//optional method defined in an interface implemented by the base class
implement componentDidMount(){
}
//abstract method defined in the base class
override render(){
}
}
assign
、 implement
、 override
はまったく同じことをしないでください:名前が存在することを確認してください(基本クラス、実装されたインターフェース、ベースによって実装されたインターフェース)クラスなど...)。
基本クラスといくつかの実装されたインターフェースの間に名前の競合がある場合、1、2、またはキーワードがまったくないかどうかに関係なく、コンパイル時エラーが発生します。
オブジェクトリテラルについても考えてみてください。
var mc = new MyComponent();
mc.state = null;
mc.componentDidMount =null;
mc.render = null;
まったく同じ構文で、フィールドまたはメソッドが基本クラス、直接インターフェイスの実装、または基本クラスに実装されているインターフェイスからのものである場合は、それらを個別に再割り当てできます。
割り当て、実装、オーバーライドはまったく同じことを行わないでください。名前が存在することを確認してください(基本クラス、実装されたインターフェイス、基本クラスによって実装されたインターフェイスなど)。
ここでは3つの異なるシナリオについて説明したので、明らかに同じではありません。 なぜ彼らが一日中あなたと違うのかを説明できると思いますが、あなたはまだそうではないと主張しているので、今のところこの特定の議論から離れます。 とにかく、TSの人たちが今でもこれを検討していると言うことは何もありません。
#6118の閉鎖に伴い、そこでの問題とここでの問題を同時に解決できるかどうかを議論する理由があると思います。
https://github.com/Microsoft/TypeScript/pull/6118を知りませんでした。 このアイデアは、 override
を追加する代わりの方法のように見えます。
問題を正しく理解した場合、問題は、互換性はあるが同一ではないメンバー宣言を持つ複数の基本クラス/インターフェイスを持つことができ、型なしの子クラスで初期化するときにそれらを統合する必要があることです。
結果についての良い考えがなくても、私は次の場合に幸せになります。
より重要なIMOは、クラスメンバーを作成するときにオートコンプリートをトリガーする方法(Ctrl + Space)を持つことです。 カーソルがクラス内に直接ある場合は、継承されたメンバーを再定義する新しいメンバーを定義している可能性があるため、オートコンプリートはあまり積極的ではありませんが、手動でトリガーすることで動作は問題ありません。
@RyanCavanaughのコメントについて:
ここでは、ユースケースを確実に理解しています。 問題は、この段階で言語に追加すると、削除するよりも混乱が増えることです。 5つのメソッドを持つクラス(そのうち3つはオーバーライドとマークされています)は、他の2つがオーバーライドされていないことを意味しません。 その存在を正当化するために、モディファイアは実際にはそれよりもきれいに世界を分割する必要があります。
変数をany
と入力しても、他の変数がany
である、またはそうでないことを意味するわけではありません。 ただし、明示的に宣言することを強制するコンパイラフラット--no-implicit-any
があります。 同様に--no-implicit-override
を用意することもできますが、それが利用可能であれば、私はこれをオンにします。
使用されるoverride
キーワードを使用すると、開発者は、慣れていないコードを読み取るときに膨大な量の洞察を得ることができ、それを強制する機能により、コンパイル時の制御がさらに強化されます。
すべての+1erについて、上記のデコレータソリューションについて十分ではないことについて話していただけますか?
デコレータがoverrideキーワードよりも優れたソリューションである方法はありますか? それが悪化する理由はたくさんあります。1)実行時のオーバーヘッドが少しでも追加されます。 2)コンパイル時のチェックではありません。 3)このコードをすべてのライブラリに含める必要があります。 4)これがoverrideキーワードを欠いている関数をキャッチする方法はありません。
例を挙げましょう。 ChildA
、 ChildB
、 Base
の3つのクラスを持つライブラリがあります。 ChildA
とChildB
にメソッドdoSomething()
を追加しました。 いくつかのリファクタリングの後、ロジックを追加して最適化し、 doSomething()
をBase
クラスに移動しました。 その間、私の図書館に依存する別のプロジェクトがあります。 ChildC
とdoSomething()
があります。 依存関係を更新して、 ChildC
が暗黙的にdoSomething()
をオーバーライドしていることを確認する方法はありませんが、最適化されていない方法で、いくつかのチェックが欠落しています。 そのため、 @overrides
デコレータでは十分ではありません。
必要なのはoverride
キーワードと--no-implicit-override
コンパイラフラグです。
override
キーワードは、プロジェクトで基本クラスの単純な階層を使用してすべてのコンポーネントを作成するため、非常に役立ちます。 私の問題は、これらのコンポーネントが別の場所で使用されるメソッドを宣言する必要があるという事実にあります。このメソッドは、親クラスで定義されている場合とされていない場合があり、必要な処理を実行している場合と実行していない場合があります。
たとえば、関数validate
が、パラメーターとしてgetValue()
メソッドを持つクラスを受け取るとします。 このクラスを作成するために、このgetValue()
メソッドをすでに定義している別のクラスを継承できますが、そのソースコードを確認しない限り、これを実際に知ることはできません。 私が本能的に行うことは、このメソッドを自分のクラスに実装することです。署名が正しい限り、誰も私に何も教えてくれません。
しかし、多分私はそれをするべきではありませんでした。 次の可能性はすべて、私が暗黙のオーバーライドを行ったことを前提としています。
super
を呼び出すことですが、誰もそうするように提案しませんでした。必須のoverrideキーワードがあると、「オーバーライドしているので、作業を行う前に元のメソッドがどのように表示されるかを確認する必要があります」と表示されます。 それは私の相続の経験を大いに改善するでしょう。
もちろん、提案されているように、これは重大な変更であり、ほとんどの人がこれらすべてについてそれほど気にしないため、 --no-implicit-override
フラグの下に配置する必要があります。
any
と--no-implicit-any
で作成された@eggersの比較は、同じ種類のアノテーションであり、まったく同じように機能するため、気に入っています。
@olmobrutall抽象メソッドとインターフェースメソッドのオーバーライドについてのあなたの話をいくつか見ていました。
私の意見では、 override
はsuper.
の存在を意味します。抽象メソッドもインターフェイスで定義されたメソッドもsuper.
呼び出しを介して呼び出すことはできないため、オーバーライドできません(オーバーライドするものはありません)。 代わりに、これらのケースに対してより明確なことを行う場合は、 implements
キーワードにする必要があります。 ただし、それは別の機能の説明になります。
これが私が見ている問題であり、最も重要なものから最も重要でないものまでランク付けされています。 私は何かを逃しましたか?
そうでない場合は、基本クラスのメソッドをオーバーライドしていると_考える_のは簡単です。
class Base {
hasFilename(f: string) { return true; }
}
class Derived extends Base {
// oops
hasFileName(f: string) { return false; }
}
これがおそらく最大の問題です。
これは、特に実装されたインターフェイスにオプションのプロパティがある場合、非常に密接に関連しています。
interface NeatMethods {
hasFilename?(f: string): boolean;
}
class Mine implements NeatMethods {
// oops
hasFileName(f: string) { return false; }
}
これは_オーバーライドの失敗_ほど重要ではありませんが、それでも悪い問題です。
基本クラスのメソッドを気付かずにオーバーライドすることが可能です
class Base {
hasFilename(f: string) { return true; }
}
class Derived extends Base {
// I didn't know there was a base method with this name, so oops?
hasFilename(f: string) { return true; }
}
可能なメソッド名のスペースが非常に大きいという理由だけで、これは比較的まれなはずです。また、互換性のあるシグニチャを作成する可能性は低く、そもそもこれを実行しているわけではありません。
any
sオーバーライドメソッドがベースのパラメータタイプを取得すると考えるのは簡単です。
class Base {
hasFilename(f: string) { return true; }
}
class Derived extends Base {
// oops
hasFilename(f) { return f.lentgh > 0; }
}
これらのパラメータを自動的に入力しようとしましたが、問題が発生しました。 hasFilename
が明示的にoverride
とマークされている場合、これらの問題をより簡単に解決できる可能性があります。
メソッドが基本クラスのメソッドをオーバーライドしている場合とそうでない場合は、すぐにはわかりません。 今日利用可能な合理的な回避策(コメント、デコレータ)があるため、これは小さな問題のように思われます。
派生クラスのオーバーライドメソッドを作成するときは、基本メソッド名を手動で入力する必要がありますが、これは面倒です。 補完リストにこれらの名前を常に指定することでこれを簡単に修正できるため(実際にはすでにバグがあると思います)、この問題を修正するための言語機能は実際には必要ありません。
これらを別の問題に属するものとしてリストするか、単に発生しないものとしてリストします。
override
と混合する必要があるものではなく、多くの優れたシナリオでは実際には望ましくないセマンティクスがありますimplements.foo = ...
)。 ここで自転車に乗る必要はありません@RyanCavanaugh私もその順序で上記のすべてに同意します。 コメントと同じように、 override
キーワードが「実装の失敗」の問題を解決するはずではないと思います。 上で述べたように、問題は別のチケットの一部として別のキーワード(たとえばimplements
)で解決する必要があると思います。 ( override
はsuper.
メソッドを意味する必要があります)
@RyanCavanaugh私はすべての点に同意しますが、型をオーバーライドせずに(そして型チェックを失うことなく)親型で宣言された初期化フィールドの非常に関連する問題への参照は見当たりません。 この機能は、メソッドがオブジェクトフィールドの単なる関数である言語でのメソッド宣言に限定されるべきではないと思います。
@eggers最後に2つのキーワードが必要な場合でも、セマンティクスは非常に似ているため、機能について一緒に説明する必要があると思います。
オーバーライドはスーパーを意味する必要があります。 方法
C#では、オーバーライドは抽象メソッド(.superなし)にも使用できます(また、使用する必要があります)。 Javaでは、 @overrideは属性です。 もちろん、それらは異なる言語ですが、同様のキーワードを使用すると、同様の動作が期待されます。
@RyanCavanaughのポイントに同意しますが、「偶発的なオーバーライド」の問題は、彼が信じているよりも少し一般的かもしれないと思います(特に、すでに何かを拡張しているReactコンポーネントのように、すでに何かを拡張しているクラスを拡張する場合はそうです)最初の拡張機能でcomponentWillMount
メソッドを定義します)。
@eggersが言ったように、「実装の失敗」の問題はかなり異なるものであり、親クラスに存在しないメソッドにoverride
キーワードを使用するのは奇妙に感じると思います。 私はこれらが異なる問題を抱えた異なる言語であることを知っていますが、C#ではoverride
はインターフェースに使用されていません。 --no-implicit-override
フラグを取得できる場合は、インターフェイスメソッドの前にインターフェイス名を付ける必要があることをお勧めします(これはC#によく似ているため、より自然に感じられます)。
何かのようなもの
interface IBase {
method1?(): void
}
class Base {
method2() { return true; }
}
class Test extends Base implements IBase {
IBase.method1() { }
override method2() { return true; }
}
@RyanCavanaughは根本的な問題をリストしていると思います。 私はそれを「不一致は何ですか」で補足します。
override
と見なされますか? 例えば: interface IDrawable
{
draw?( centerPoint: Point ): void;
}
class Square implements IDrawable
{
draw( centerPoint: Point ): void; // is this an override?
}
override
と見なされるインターフェイスに一致するプロパティを宣言していますか? interface IPoint2
{
x?: number;
y?: number;
}
class Circle implements IPoint2
{
x: number; // is this an override?
y: number; // is this an override?
radius: number;
}
override
キーワードの代わりに上記のケースを処理するために、ある種のimplements
キーワードと、それがとる形式が必要ですか。@kevmeister68意見の相違を明示的に述べてくれてありがとう。
1つ:問題を実際に示すには、インターフェイスメンバーをオプションにする必要があります。このように、フィールドまたはメソッドが実装されていない場合、typescriptコンパイラは「実装の失敗」を解決し、文句を言います。
@olmobrutallありがとうございます。 私はメソッドをオプションとして宣言したことはありません-それは可能ですか(私はC#のバックグラウンドから来ており、そのような概念はありません)? 上記の例を更新して、メンバー変数をオプションに変更しました-それは良いですか?
@ kevmeister68 IDrawable
のdraw
メソッドも:)
@RyanCavanaugh
不十分なエキゾチックな構文(例:implements.foo = ...)。 ここで自転車に乗る必要はありません
新しい表現を教えてくれてありがとう。 機能のセマンティクスに多くの問題は見られません。
問題のほとんどは、それらが暗示する構文と動作に依存しています。
ツリーをより有望な構文と比較してみましょう。
class Person{
dateOfBirth: Date;
abstract talk();
walk(){ //...}
}
interface ICanFly{
fly?();
altitude?: number;
}
class SuperMan extends Person implements ICanFly {
dateOfBirth = new Date(); //what goes here?
override talk(){/*...*/}
walk = () => {/* force 'this' to be captured*/} //what goes here
implements fly() {/*...*/}
altitude = 1000; //what goes here?
}
利点:
extends
/ implements
と比較すると正しいと感じます。短所:
override
またはimplements
が正しいと感じるものはありません。 たぶんsuper.
ですが、それではインターフェースで何をしますか?function()
のラムダ(これをキャプチャするのに便利)を使用してメソッドをオーバーライドすると、同じ問題が発生します。InterfaceName.
class Person{
dateOfBirth: Date;
abstract talk();
walk(){ //...}
}
interface ICanFly{
fly?();
altitude?: number;
}
class SuperMan extends Person implements ICanFly {
dateOfBirth = new Date(); //what goes here?
override talk(){/*...*/}
walk = () => {/* force 'this' to be captured*/} //what goes here
ICanFly.fly() {/*...*/}
ICanFly.altitude = 1000; //what goes here?
}
利点:
super.
を使用できます。短所:
this.
class Person{
dateOfBirth: Date;
abstract talk();
walk(){ //...}
}
interface ICanFly{
fly?();
altitude?: number;
}
class SuperMan extends Person implements ICanFly {
this.dateOfBirth = new Date();
this.talk(){/*...*/}
this.walk = () => {/* force 'this' to be captured*/}
this.fly() {/*...*/}
this.altitude = 1000;
}
利点:
this.
を使用してオートコンプリートをトリガーするのは自然なことです。短所:
オプションではないインターフェイスメソッドの実装に失敗するとコンパイラエラーが発生しますが、将来のある時点でインターフェイスが変更された場合(たとえば、メソッドが削除された場合)、そのメソッドを実装したすべてのクラスに警告は表示されません。 これはオプションの問題ほど大きな問題ではないかもしれませんが、これの範囲はそれらを超えていると言っても過言ではありません。
ただし、 override
は明らかに異なるものであり、1つではなく2つのキーワードを使用する方が、プログラムの意図をより適切に伝えることができると私は信じています。
たとえば、開発者が、基本クラスですでに宣言されているメソッドを実際にオーバーライドしているときに、インターフェイスメソッドを実装していると_信じている_シナリオを考えてみます。 2つのキーワードを使用すると、コンパイラはこの種の間違いを防ぎ、 method already implemented in a base class
のトーンでエラーをスローできます。
interface IDelegate {
execute?() : void;
}
class Base implements IDelegate {
implement public execute() : void { /// fine, this is correctly implementing execute
}
}
class Derived extends Base {
implement public execute() : void {
/// ERROR: `method "execute():void" already implemented in a base class`
}
}
これがJSに関連するかどうかはわかりませんが、クラスフィールドはOOでプライベートであると想定されているため、フィールドがプライベートであるために完全にオーバーライドできないため、フィールドを明示的にオーバーライドする必要はないと思います。 。
ただし、インスタンスメソッドはフィールドであり、私が収集したものから、多くの人がプロトタイプメソッドの代わりにインスタンスメソッドを使用しています。 そのことについて、
class Person{
walk(){ //...}
}
class SuperMan extends Person {
walk = () => {/* force 'this' to be captured*/}
}
walk
はPerson
のプロトタイプメソッドであり、 SuperMan
のインスタンスメソッドであるため、はコンパイラエラーです。
とにかく、ここでoverride
が適切かどうかはわかりません。これは、フィールドをオーバーライドしないためです。 繰り返しになりますが、C#のように見えますが、ここではoverride
new
キーワードを使用したいと思います。 これはメソッドのオーバーライドと同じではないためです(ここではなく、オーバーライドでsuper.myMethod
を呼び出すことができます)。
私の好ましい解決策は次のようになります(厳密なオーバーライドモードにあると仮定します):
class Person{
dateOfBirth: Date;
talk() { }
walk = () => { }
}
interface ICanFly {
fly?();
altitude?: number;
}
class SuperMan extends Person implements ICanFly {
new dateOfBirth = new Date();
override talk() { }
new walk = () => { }
implements fly() {/*...*/}
implements altitude = 1000;
}
私の主な関心事はインターフェースについてです。 オプションではないインターフェイスメンバーにはimplements
を記述する必要はありません。これは、コンパイラーによって既にキャッチされているためです。 次に、そうすると、すべてのインターフェイスメンバーの前にimplements
が付けられるわけではないため、インターフェイスからのものとそうでないものの間に混乱が生じます。 そして、その言葉は一口です。 私はこのようなものを書く準備ができていません:
class C extends React.Component {
implements componentWillMount() { }
implements componentDidMount() { }
implements componentWillReceiveProps(props) { }
/// ... and the list goes on
}
以前にインターフェース名のプレフィックスを付けることを提案しましたが、 @olmobrutallはそれがより悪い考えであることを示しました。
とにかく、これに適切に取り組むには、3つの異なる新しいキーワードが必要になると私はかなり確信しています。
また、コンパイラーは互換性のないものを書くことをすでに妨げているので、オーバーライドを暗黙的に入力する必要はないと思います。特に、正しく行うのは明らかに難しいためです。
new
、 override
、およびimplement
は、本質的に同じJSセマンティクスに対してキーワードのオーバーヘッドが多すぎませんか? C#でそれらが異なっていても、JS/TSに実質的な違いはありません。
また、かなりあいまいです。
new
であるのに、インターフェースでimplements
なのですか?override
、 implements
、2つのうちのいずれか、または両方を同時に?これについても考えてください:
class Animal {
}
class Human extends Animal {
}
class Habitat {
owner: Animal;
}
class House extends Habitat {
owner = new Human();
}
var house = new House();
house.owner = new Dog(); //Should this be allowed??
質問は:
owner = new Human();
は、新しい(ただし互換性のある)タイプでフィールドを再定義しますか、それとも単に値を割り当てますか?
_magicキーワード_を使用する場合を除いて、一般的にフィールドを再定義したと思います。 私の好みは今this.
です。
class House extends Habitat {
this.owner = new Human(); //just setting a value, the type is still Animal
}
@olmobrutallこれは確かにキーワードのオーバーヘッドが少し多いかもしれませんが、3つすべてが実際に_異なる_ものです。
override
は(プロトタイプで定義された)メソッドに適用されます。つまり、元のメソッドは消去されません。これは、 super
の背後から引き続きアクセスできます。new
はフィールドに適用され、元のフィールドが新しいフィールドによって消去されたことを意味します。implements
はインターフェースからのすべてに適用され、インターフェースが「存在しない」ため、親クラスにすでに存在するものを消去または置換しないことを意味します。インターフェイスで実装された基本クラスからメソッドを変更する場合は、何を使用する必要がありますか? 私は4つの可能性を見ることができます:オーバーライド、実装、2つのうちのいずれか、または両方を同時に?
あなたがここで効果的に行っていることなので、それがoverride
になることは私にはかなり論理的に思えます。 implements
は、他の方法では存在しないインターフェイスから何かを実装していることを意味します。 ある意味で、 override
とnew
はimplements
よりも優先されます。
そして、あなたのフィールドオーバーライドの例については、それが今日どのように機能するかについて何も変わらないはずです。 ここで何が起こるかはわかりませんが、それが何であれ、変更されるべきではありません(または、変更されるべきかもしれませんが、ここで説明していることではありません)。
override
は、抽象メソッド(以下で説明)を含め、基本メソッドとプロパティの両方に適していると思います。
new
は概念的には興味深いアイデアですが、この特定のキーワードはクラスのインスタンス化と混同される可能性があるため、プロパティがプリミティブ、インターフェイス、ユニオンを持つことができるという事実にもかかわらず、クラスでのみ機能するという誤った印象を与える可能性がありますまたは関数タイプですら。 おそらくreassign
のような別のキーワードの方がうまくいくかもしれませんが、値が再宣言されただけで、派生クラス( new
にもその問題がある可能性があります)。 redefine
も興味深いと思いますが、「redefined」プロパティもベースプロパティとは異なるタイプである可能性があるという誤った予想につながる可能性があるため、わかりません。(_edit:実際にチェックしましたまた、新しいタイプがベースタイプのサブタイプである限り、異なるタイプを持つ可能性があるため、これはそれほど悪いことではない可能性があります_)。
implement
( override
との一貫性を保つために、この特定の動詞形式を好みます)はインターフェイスで機能するようです。 技術的には抽象ベースメソッドでも機能すると思いますが、セマンティクスがわずかに異なるにもかかわらず、 override
を使用すると一貫性が増す可能性があります。 もう1つの理由は、メソッドを抽象クラスから非抽象クラスに変更するのが少し不便になることです。必要に応じて、すべての派生クラスに移動し、 override
をimplement
に変更します。
もっと良いアイデアがあるかもしれませんが、現時点で私が持っているのはそれだけです。
キーワードnew
は、C#がこの正確な機能に使用するものであるため提案しましたが、これが最良の機能ではないことに同意します。 implement
は確かにimplements
よりも良い選択です。 抽象メソッドはインターフェイスではなく基本クラスの一部であるため、抽象メソッドに使用するべきではないと思います。 override
+ new
->基本クラス、 implement
->インターフェース。 それはより明確に思えます。
@JabX
私はチェックしました、そしてあなたは正しいです:
class Person{
walk(){ //...}
}
class SuperMan extends Person {
walk = () => {/* force 'this' to be captured*/}
}
コンパイラエラーで失敗する: Class 'Person' defines instance member function 'walk', but extended class 'SuperMan' defines it as instance member property.
親タイプのコントラクトが実行され、とにかくこれを書くことができるので、この場合に失敗する理由は実際にはわかりません。
var p = new SuperMan();
p.walk = () => { };
あるいは
class SuperMan extends Person {
constructor() {
super();
this.walk = () => { };
}
}
override
オーバーライドは(プロトタイプで定義された)メソッドに適用されます。つまり、superの背後で引き続きアクセスできる元のメソッドは消去されません。
それは実際には議論です。 override
とimplements
の間には、観察可能な違いはほとんどありませんが、完全ではありません。
override
とimplements
の両方がprototype
に実装されており、$だけでなくsuper()
super.walk()
を呼び出すため、 super
を使用できるかどうかは独立しています。 super()
であり、すべてのメソッド(オーバーライド、実装、ニュース、および通常の定義)で使用できます。
class SuperMan extends Person implements ICanFly {
new dateOfBirth = new Date();
override talk() { } //goes to prototype
new walk = () => { }
implements fly() {/*...*/} //also goes to the prototype
implements altitude = 1000;
}
また、インスタンスに割り当てると、 super.walk()
を使用できるようになります。
class SuperMan extends Person {
constructor() {
super();
this.walk = () => { super.walk(); };
}
//or with the this. syntax
this.walk = () => { super.walk(); };
}
prototype
フィールドをインスタンスフィールドと区別する明確な方法がすでにあり、それは=
トークンです。
class SuperMan extends Person implements ICanFly {
this.dateOfBirth = new Date(); //instance
this.talk() { } //prototype
this.walk = () => { } //instance
this.fly() {/*...*/} //prototype
this.altitude = 1000; //instance
}
this.
構文を使用すると、問題はよりエレガントに解決されます。
this.
は、私のタイプ(基本クラスと実装されたインターフェース)をチェックすることを意味します。=
は、プロトタイプではなくインスタンスに割り当てることを意味します。New
new
はフィールドに適用され、元のフィールドが新しいフィールドによって消去されたことを意味します。
型システムを壊してしまうため、フィールドやメソッドを別の型に変更することはできないことに注意してください。 C#またはJavaでは、 new
を実行すると、新しいタイプへの静的にディスパッチされた呼び出しでのみ新しいメソッドが使用されます。 Javascriptでは、すべての呼び出しが動的になり、タイプを変更すると、実行時にコードが破損する可能性があります(ただし、タイプの強制は実用的な目的で許可される可能性がありますが、拡張は許可されません)。
class Person {
walk() { }
run() {
this.walk();
this.walk();
this.walk();
}
}
class SuperMan extends Person {
new walk(destination: string) { } //Even if you write `new` this code will break
}
したがって、 new
を超える場合、キーワードはassign
である必要があります。これは、これが実行できる唯一のタイプセーフなことだからです。
class Person{
dateOfBirth: Date;
}
class SuperMan extends Person implements ICanFly {
assign dateOfBirth = new Date();
}
reassign
は意味をなさないことに注意してください。これは、Personがフィールドを宣言するだけで、値を設定しないためです。
assign
書くのは冗長に思えますが、反対側に=
があるので、割り当てていることは明らかです。
ここでも、 this.
構文は問題を適切に解決します。
class Person{
dateOfBirth: Date;
}
class SuperMan extends Person implements ICanFly {
this.dateOfBirth = new Date();
}
これを適切な方法でフィールドに適用する方法がわかりません。
クラス本体にのみ適用可能なキーワードを追加することについて話しているのですが、インスタンスの存続期間中、フィールドは_任意のポイント_で再割り当てできます。
Typescriptを使用すると、クラス本体のフィールドを初期化できます。
class Person {
dateOfBirth : new Date();
}
また、親クラスで宣言されたフィールドを再初期化することもできますが、状況は良くありません。 もちろん、名前を間違って書くこともできますが( override
と同様の問題)、型も消去されます。 この意味は:
class Person {
fullName: { firstName: string; lastName?: string };
}
class SuperMan extends Person {
fullName = { firstName: "Clark" };
bla() {
this.fullName.lastName; //Error
}
}
このコードは次のように失敗します。プロパティ'lastName'はタイプ'{firstName:string;に存在しません。
クラス本体にのみ適用可能なキーワードを追加することについて話していますが、フィールドはインスタンスの存続期間中の任意の時点で再割り当てできます。
また、方法:
class Person {
talk() { }
}
class SuperMan extends Person {
talk() { }
changeMe(){
this.talk = () => { };
}
changeMyPrototype() {
SuperMan.prototype.talk = () => { };
}
}
@kungfusheep同意します。フィールドは再割り当てできますが、プロトタイプのメソッドも再割り当てできます。
「オーバーライド」フィールドのキーワードが必要になる主な理由は、インスタンスメソッドがフィールドであり、ラムダであり、コンテキストを自動バインドするため、適切なプロトタイプメソッドの代わりに広く使用されているためです。
@olmobrutall
それは禁止されており、私が信じる正当な理由があります(同じことではないので、互換性があるべきではありません)が、クラスのインスタンスを直接操作するときにそうすることが可能であるのは正しいです。 なぜ許可されているのか正確にはわかりませんが、言語の動作を変更するためにここにいるわけではないので、ここで干渉するべきではないと思います。
クラスインターフェイスは、クラスのインスタンス側、つまりプロトタイプのフィールドとメソッドを記述します。 インターフェイスのインスタンスメソッドとプロトタイプメソッドに違いはないと思います。メソッドをどちらか一方として記述し、どちらか一方として実装できると思います。 したがって、 implement
はフィールドにも適用できます(そして適用する必要があります)。
new
は、 override
と同じセマンティクスを持つ必要があるため、互換性のないタイプに変更することはできません。 つまり、言語の動作方法は変更していません。 同じ種類の再定義ではないので、別のキーワードが必要でした。 しかし、あなたが私に示したように、 =
を使用することで、フィールドとメソッドを区別できることに同意します。したがって、同じキーワード/構文を使用しても、あいまいさはありません。
ただし、統一フィールド/メソッドのオーバーライド構文は、有効なJavascriptである可能性があるものによく似ているため、 this.method()
またはthis.field
であってはなりません。 メンバーの前にキーワードを追加すると、それが実際にTypescriptのものであるという事実に向かってより明確になります。
this
は良い考えですが、ドットを削除して、次のようなあいまいさの少ないものを書くことができるかもしれません。
class Superman {
this walk() { }
}
しかし、それでも、それは奇妙に見えます。 そして、それをimplement
と混合すると、少し場違いに見えます(このthis
構文はインターフェイスで使用されないという事実で大丈夫ですか?)、そして私はimplement
が好きですoverride
デュオ。 フィールドのoverride
修飾子はまだ私を苛立たせますが、3番目のあいまいなnew
キーワードを使用する方が良いかもしれません。 割り当て( =
)はメソッドとフィールドの違いを明確にするので、(数時間前に言っていたのとは対照的に)今はそれで問題ありません。
同意します。フィールドは再割り当てできますが、プロトタイプのメソッドも再割り当てできます。
ええ、TypeScriptでプロトタイプを直接変更することは、内部をいじり回すことから離れた領域にあります。 何かのインスタンスにプロパティを単に再割り当てするほど一般的ではありません。 15万行以上のコードベースでフィールドを関数として使用することはめったにないので、広く使用されているのは主観的なものかもしれません。
私はあなたの他のほとんどの点に同意しますが、この文脈でのnew
キーワードの使用については売られていません(そしてその音ではあなたもそうではありません...)。
15万行以上のコードベースでフィールドを関数として使用することはめったにないので、広く使用されているのは主観的なものかもしれません。
私もそうではありませんが、メソッドでthis
を適切にバインドするには、 @autobind
デコレータを使用する必要があります。これは、単にラムダを作成するよりも面倒です。 また、継承を使用しない限り、フィールドの使用は期待どおりに機能します。 少なくともJS/Reactの世界では、ES6クラスを使用しているほとんどの人がメソッドとして矢印関数を使用しているという印象を受けました。
また、インターフェイスメソッドは、適切なメソッドまたはインスタンスメソッドのいずれかを使用して実装できますが、違いを明確にするのに役立ちません。
オーバーライドに関する限り、フィールドにはメソッドと同じ問題があると思います。
class A {
firstName: string;
get name() {
return this.firstName;
}
}
class B extends A {
firstname = "Joe" // oops
}
ここで考えられる回避策は、コンストラクターでフィールドを割り当てることです。
class B extends A {
constructor() {
this.firstName = "Joe"; // can't go wrong
}
}
ただし、これはインターフェイスには適用されません。 そのため、私(およびここにいる他の人)は、クラス本体で直接宣言するときに、フィールドの存在を確認するための何かが必要であると信じています。 そして、それはoverride
ではありません。 ここでもう少し考えてみると、フィールドの問題はインターフェイスメンバーの問題とまったく同じだと思います。 親クラスにすでに実装されているメソッド(プロトタイプメソッド、場合によってはインスタンスメソッド)にはoverride
キーワードが必要であり、定義されているが実装されていないもの(非関数)には別のキーワードが必要だと思います。フィールドが含まれています)。
私の新しい提案は、暫定的なmember
キーワードを使用することです(おそらく最良の選択ではありません):
interface IBase {
interfaceField?: string;
interfaceMethod(): void
}
abstract class Base {
baseField: number;
baseMethod() { }
baseLambda: () => { };
abstract baseAbstractMethod();
}
class Derived extends Base implements IBase {
member interfaceField = "Hello";
member interfaceMethod() { }
member baseField = 2;
override baseMethod() { }
override baseLambda = () => { };
member baseAbstractMethod() { }
}
override
は実装を伴うものであり、既存の動作を置き換えることを示しているので、抽象メソッドにはmember
キーワードを選択することに注意してください。 インスタンスメソッドは、特別な種類のフィールドであるため、 override
を使用する必要があります(メソッドシグネチャを実装できるため、コンパイラによってすでに認識されています)。
TypescriptはJavaScriptのコンパイル時チェックバージョンである必要があり、TSを学習することで、C#を学習するのと同じように、JSも学習する必要があると思います。CLRをよく理解できます。
プロトタイプの継承はJSのクールな概念であり、非常に中心的なものです。TSは、ここではあまり意味をなさない他の言語の概念でそれを隠そうとすべきではありません。
メソッドまたはフィールドを継承している場合、プロトタイプの継承は何の違いもありません。
例えば:
class Rectangle {
x: number;
y: number;
color: string;
}
Rectangle.prototype.color = "black";
ここでは、プロトタイプオブジェクトに単純なフィールドを設定しているため、インスタンスフィールドがなくても、デフォルトですべての長方形が黒になります。
class BoundingBox {
override color = "transparent"; // or should be member?
}
また、 member
キーワードは、クラスの他のメンバーを嫉妬させます。
必要なのは、クラス宣言コンテキストで、オブジェクトリテラルまたはメンバー式にすでにあるのと同じ種類の動作(コンパイル時のチェック/オートコンプリート/名前の変更)を可能にする構文です。
たぶんthis.
のより良い代替手段は.
です:
class Derived {
.interfaceField = "hello";
.interfaceMethod() {}
.baseField = 2;
.baseMethod() {}
.baseLambda = () => {};
.baseAbstractMethod(){};
someNewMethod(){}
someNewField = 3;
}
結論として、Typesystemは、どの値がプロトタイプからのもので、どの値がインスタンスからのものであるかを追跡する必要はないと思います。これは、プロトタイプに強制的にアクセスできるようになると難しい問題であり、問題を修正することはできないためです。 JSの表現度を制限します。
したがって、関数フィールド、矢印関数フィールドとメソッド、型システム、生成されたコード( this
が使用されていない場合)に違いはありません。
やあみんな、これは面白いものですが、具体的にはoverride
の観点からはかなり正直です。 これらのコメントを元の提案にもっと具体的にリンクすることができない限り、この種のことについていくつかの新しいディスカッションの問題が発生することを嬉しく思います。
ここで言っていることの多くは、実際にはTypeScriptに固有のものではないため、ESDiscussスレッドを開始することも適切な場合があります。 確かに、彼らはこの種のことについて同じように考えてきました(プロトタイプとインスタンスで何が起こるかという観点から)。
@olmobrutall
ES6クラスはすでにプロトタイプのものを隠しており、 @ kungfusheepが言ったように、それを直接いじることは正確に通常のことではありません。
したがって、関数フィールド、矢印関数フィールドとメソッド、型システム、生成されたコード(これが使用されていない場合)に違いはありません。
生成されたコードでは、クラスメソッドがプロトタイプに配置され、他のすべて(プレフィックスがstatic
ではない)がインスタンスに配置されます。これにより、継承に違いが生じます。
とにかく、私は今、両方の種類のメソッドに別々の構文を用意する必要がないことに同意しますが、 override
キーワードを使用する場合は、メソッドに限定する必要があり、見つける必要があります残りのために何か他のもの。 すべてに固有のキーワードがあると便利かもしれませんが、その意味については非常に明確である必要があります。 override
は明確ですが、メソッドの場合のみです。 あなたは構文をドットします、これはthis.
のようにまだ私の好みには既存のJSに近すぎます。
@RyanCavanaugh
override
の観点からは、接線から外れています。
オーバーライドに関する私の問題は、インターフェースのメソッドとフィールドに対して十分に一般的ではないということです。 3つのオプションがあります。
override
を使用します。override
を使用しますmember
、 implement
、 this.
、 .
など...)この決定はここで行われるべきだと思います。
あなたが言っていることの多くは、実際にはTypescriptに固有のものではありません
絶対! 私たちは皆、トランスパイルの現在の状態に満足していますが、タイプチェック/ツールについては満足していません。
キーワードが何であれ、それはTypescriptのみになります(抽象的と同じように)
@JabX
もちろん、生成されたコードについては正しいです。 私のポイントは、次のように書くことができるということでした。
class Person {
name: string = "John";
saySomething() {
return "Hi " + this.name;
}
}
クラスを宣言します。 name
はインスタンスに移動し、 saySomething
はプロトタイプに移動します。 それでも私はこれを書くことができます:
Person.prototype.name = "Unknown";
Person.prototype
のタイプは人全体だからです。 Typescriptは、単純化のために、型システムのどこに何が起こっているかを追跡しません。
すべてに固有のキーワードがあると便利かもしれませんが、その意味については非常に明確である必要があります。
私が考えることはより重要であり、私たち全員が同意できるのはセマンティクスです。
変更されXXX
は、メンバーが基本クラスまたは実装されたインターフェイスですでに宣言されていることを確認します。通常、基本クラスの関数をオーバーライドするために使用されます。
.
またはthis.
はあまりにも異質に見え、 member
は冗長であるため、すべてのケースでoverride
を悪用するのがおそらく最善の選択だと思います。 フィールドの設定やインターフェイスメソッドの実装は、とにかく副次的な機能です。
多くの前例があります:
static class
は実際には_オブジェクトのクラス_ではなくなりました。static
フィールドについては_static_はありません。virtual
メソッドはかなり_具体的_です(VBはOverrideable
を使用します)。次のようになります。
class Person{
dateOfBirth: Date;
abstract talk();
walk(){ //...}
}
interface ICanFly{
fly?();
altitude?: number;
}
class SuperMan extends Person implements ICanFly {
override dateOfBirth = new Date();
override talk(){/*...*/}
override walk = () => {/* force 'this' to be captured*/}
override fly() {/*...*/}
override altitude = 1000;
}
私たちはそれで解決できますか?
インターフェースから来るものには別のimplement
キーワードが必要です(両方にある場合override
が優先されます)。これは、オーバーライドではなく実装です。
それ以外の場合は、親クラスのすべてにoverride
を悪用するのが最善であることに同意します。
ただし、 implement
とoverride
の間に意味上の違いはありません。
どちらもJSと同様のオートコンプリート/エラーメッセージ/トランスパイルが発生します...これは哲学的な違いにすぎません。
2つのキーワードを説明することは本当に価値があり、ペダンティングコンパイラはError you should use 'override' instead of 'implement'
を教えてくれます。
この場合はどうですか?
interface IComparable {
compare(): number;
}
class BaseClass implements IComparable {
implement compare();
}
class ChildClass extends BaseClass implements IComparable { //again
override compare(); // or implements...
}
質問は...誰が気にしますか?
また、 implement
/ implements
の問題があります。
override
を悪用しましょう。 1つのコンセプト1つのキーワード、これは重要なことです。
さて、私はすでにoverride
をimplement
よりも優先することを提案しましたが、おそらく十分に明確ではありませんでした。
私はまだこれが同じことだとは思わない。 別の例:必須のインターフェイスメンバーの場合、実装の失敗はコンパイラエラーですが、オーバーライドの失敗は問題ではありません(ベースメンバーが抽象である場合を除く)。
親クラスで宣言されていないもののoverride
が意味をなさないと思います。 しかし、たぶん私だけが区別を望んでいます。 いずれにせよ、私たちがこれに使用することになったキーワードが何であれ、私たちは何かに落ち着き、その使用を強制するための厳密なモードを提供できることを願っています。
さて、私はすでにオーバーライドを実装よりも優先することを提案しましたが、おそらく十分に明確ではありませんでした。
確かに、4つのケースがあると言いたかっただけです。 基本クラス、インターフェイス、基本クラスのインターフェイス、BaseClassのインターフェイスの再実装。
@JabX
親クラスで宣言されていないものをオーバーライドしても意味がないと思います。 しかし、多分私は区別を望んでいる唯一の人です。
あなたではない。 これらは根本的に異なる2つのものであり、言語レベルでその分離を行うことには利点があります。
この新機能でインターフェースがサポートされるのであれば、 override
にボルトで固定された中途半端なソリューションにはなり得ないと思います。 同じ理由で、 extends
$はクラスレベルでimplements
とは別のエンティティとして存在するため、 override
はimplement
に沿ったものと順番にペアリングする必要がありますこの問題を修正します。 それは_機能の置き換え_と_機能の定義_です。
@olmobrutall
2つのキーワードを説明することは本当に価値があり、ペダンティングコンパイラは次のように指示します。エラー「実装」ではなく「オーバーライド」を使用する必要があります。
私は今、この区別を約4回行ったように感じますが、これは衒学的ではありません。 それは本当に重要な情報です!
あなた自身の例を考えてみましょう
interface IComparable {
compare(): number;
}
class BaseClass implements IComparable {
implement compare();
}
class ChildClass extends BaseClass implements IComparable { //again
override compare(); // or implements...
}
'または実装...'は、この場合の完全に誤った仮定です。 拡張している基本クラスと同じインターフェースを実装するようにコンパイラーに指示したからといって、すでにcompare
を実装しているという事実は変わりません。
override
の代わりにChildClassでimplement
を書く場合は、無意識のうちにワイプしようとしていたことが大したことなので、コンパイラが誤った仮定を通知することに感謝する必要があります。以前に実装されたメソッドを出します!
私が担当しているコードベースでは、間違いなく大きな問題になります。 したがって、このような開発者のミスを防ぐことができるコンパイラ機能を歓迎します。
@kungfusheep
オーバーライドではなくChildClassで実装を作成する場合は、以前に実装されたメソッドを無意識のうちに消去しようとしていたことが大したことなので、コンパイラが誤った仮定を通知することに感謝する必要があります。
すでに実装されているメソッドをオーバーライドすることが非常に重要な決定である場合は、抽象メソッドにもimplement
を使用する必要があります。 メソッドをabstract
からvirtual
に変更する場合、またはその逆の場合は、実装されているすべてのバージョンをチェック(および変更)して、 super
を呼び出すか、メソッドを削除することを検討する必要があります。
実際には、これはC#では問題になりませんでした。
同意します。実装されたメソッドの場合override
、実装されていないメソッド(抽象/インターフェース)の場合implements
です。
はい、抽象化に使用できます。
C#では、「オプションの」インターフェイスシナリオがなかったため、おそらくそれほど問題とは見なされませんでした。
しかし、私のポイントは、C#ではoverride
が実装されたメソッドと実装されていないメソッドであり、誰かが不満を言うのを聞いたことがないということです。
機能の説明を少なくとも2倍難しくするに値する問題ではないと思います。
implement
キーワードが必要な唯一の理由は、インターフェイスメンバーをオプションにすることができるのに対し、C#(およびおそらくほとんどのオブジェクト指向言語でも)では不可能だからです。 インターフェイスを完全に実装することはできないため、そのためのキーワードはありません。したがって、問題はありません。 ここではさまざまな問題があるため、「C#でも同じ」という議論に基づいて議論しないでください。
override
は、抽象であるかどうかに関係なく、_baseclass_メソッドに使用されます。 区別は存在ではなくメソッド(クラスまたはインターフェース)の起源にあるべきだと私は信じているので、ここでも同じ(抽象メソッドの場合はimplement
$ではなくoverride
)にする必要があると思います実装の。 そして将来、抽象メソッドにデフォルトの実装を与えることにした場合、キーワードを置き換えるためにコード(さらに悪いことに、クラスを使用している他の人のコード)を調べる必要はありません。
私の主な質問は、厳密なoverride
/ implement
フラグを設定する必要があるか(これが完全に必要です)、必須のインターフェイスメンバーにimplement
キーワードを強制する必要があるかどうかです。 それはあまり役に立たず(そうでなければコンパイルされないため、これらの実装に失敗することはありません)、多くの不要な冗長性につながる可能性があるためです。 しかし一方で、一部のインターフェイスメンバーにimplement
を設定するのは騙されているかもしれませんが、すべてのインターフェイスメンバーに設定されているわけではありません。
@JabX基本クラスが抽象メソッドの実装を追加した場合、個人的に警告が必要です。
しかし、私はそもそも実装キーワードのアイデアで100%売られているわけではありません。 何かを実装しなかった場合、コンパイラはすでに警告を発します。 それが本当に役立つ唯一の場所は、オプションのメソッドです。
そしてとにかく、それは私がこのスレッドにいる理由とは何の関係もありません。 親クラスのオーバーライドとして関数を指定する方法があるかどうかを探していました。
私の主な質問は、厳密なオーバーライド/実装フラグを設定する必要があるか(私はこれが完全に必要です)、必須のインターフェイスメンバーにimplementキーワードを強制する必要があるかどうかです。
書かないのは警告だと思います。
それが本当に役立つ唯一の場所は、オプションのメソッドです。
ええ、それがここでの要点です。
これがないと、オーバーライドしたいタイプタイプのメソッドに対する非常に一般的なエラーですが、実際には新しいメソッドを作成しています。 このようなキーワードの導入は、関数をオーバーライドする意図を指摘する唯一の方法です。
それは警告か何かかもしれませんが、現在それは非常に苦痛です。
alm.tools:rose :のUMLクラスビューにオーバーライドアノテーションを追加しています。
また、 almの基本クラスメンバーをオーバーライドするクラスメンバーのガターインジケーターも追加しました。
スコープソリューションのPRを受け入れることで、最小の複雑さで最大の価値を実現できると考えています。
override
は、クラスメソッドとプロパティ宣言(get / setを含む)で有効ですoverride
が必要です。get
とset
の両方に、どちらかがあればoverride
のマークを付ける必要がありますoverride
があるとエラーになります--noImplicitOverride
(ここで名前を自由に自転車に乗せてください)により、オーバーライドされるものにはoverride
が必須になりますdeclare class
または.d.tsファイル)では効果がありません。現時点では範囲外です:
@RyanCavanaugh
私は現在これを実装しようとしています(始めたばかりで、特にテストのために、これに関する助けを歓迎します)が、背後にあるものを理解しているかどうかはわかりません
すべての署名(実装を含む)には、
override
が必要です。
クラス内にメソッドの複数のシグニチャを持つことはできないので、継承コンテキストについて話しているのですか? つまり( override
の使用を強制するフラグがない場合、それ以外の場合は明らかにエラーです)
class A {
method() {}
}
class B extends A {
override method() {}
}
class C extends B {
method() {}
}
method
$の前に$ override
を記述しなかったため、クラスCでエラーを出力する必要がありますか?
その場合、なぜそれを強制したいのかわかりません。 そして、それはまた逆に働くべきですか? BではなくCでoverride
を指定した場合、クラスBでエラーを出力しますか?
はい、継承ツリーの署名に適用されると思います。
そして、それが逆に機能するとは思わない。階層内のレベルで使用されると、オーバーライドルールの適用を開始する必要があります。 場合によっては、オーバーライドを適用する決定は、開発者が所有していないクラスから派生したクラスに対して行われることがあります。 したがって、一度使用すると、すべての派生クラスにのみ適用されます。
私はについて別の質問があります
get
とset
の両方に、どちらかがあればoverride
のマークを付ける必要があります
親クラスがゲッターのみを定義し、派生クラスでセッターを定義したい場合はどうなりますか? このルールに従うと、セッターをオーバーライドとして定義する必要がありますが、親クラスにセッターがないため、存在しないものをオーバーライドするとエラーになります。 まあ、実際には(私の現在の素朴な実装では)、ゲッターとセッターは同じプロパティのためのものなので、矛盾はありません。
私は次のようなものを書くことさえできます:
class A {
get value() { return 1; }
}
class B extends 1 {
override set value(v: number) {}
}
それは私には明らかに間違っているように見えます。
クラス内にメソッドの複数のシグニチャを持つことはできないため
同じメソッドに対して複数のオーバーロードシグニチャを持つことができます。
class Base { bar(): { } }
class Foo extends Base {
// Must write 'override' on each signature
override bar(s: string): void;
override bar(s?: number): void;
override bar(s: string|number) { }
}
他のケースについてのあなたの直感は正しいと思います。
ゲッターとセッターに関しては、 override
はプロパティスロットにのみ適用されると思います。 対応するゲッターなしでオーバーライドセッターを作成することは明らかに非常に疑わしいですが、 override
が存在する場合は多かれ少なかれ疑わしいことではありません。 基本クラスがどのように実装されたかは常にわからないので、ルールは単純に、 override
ゲッターまたはセッターには対応する基本クラスプロパティが_some_必要であり、 override get {
と言うと、 set
宣言にもoverride
が必要です(またはその逆)。
そうですね、そのようなクラスでメソッドシグネチャを記述できるとは知りませんでした。
virtual
も理にかなっていると思います。
すべてのキーワード: virtual
、 override
、 final
は、特にクラスをリファクタリングするときに実際に大きな違いをもたらします。
私は頻繁に継承を使用しているので、「誤って」メソッドをオーバーライドするのはとても簡単です。
virtual
は、overrideキーワードの後に抽象/仮想メソッドしか提案できないため、インテリセンスも改善します。
これらを優先してください。 ありがとう!
@pankleksに同意します-私は継承を頻繁に使用していますが、直接何も指定せずに関数をオーバーライドするのは間違っていると感じています。
「仮想」、「オーバーライド」、「最終」は完璧です。
これは私にとって重要な問題です。
@RyanCavanaugh
パラメーターまたは初期化子のコンテキスト入力(これを以前に試しましたが、これは惨事でした)
これについて詳しく説明していただけますか? TSがメソッドのオーバーライドでパラメーターのタイプを推測しない理由を調べようとしたときに、この問題を実際に見つけました。これは私にとって驚きでした。 オーバーライドするときに、同一でないパラメーターリストが正しい場合、または互換性のない戻りタイプがいつあるかわかりません。
@pankleks
virtual
も理にかなっていると思います。
その性質上、JSではすべてが「仮想」です。 final
のサポートで十分です。オーバーライドしてはならないメソッド(またはプロパティ)がある場合は、そのようにマークして、違反したときにコンパイラエラーをトリガーできます。 それならvirtual
必要ありませんよね? しかし、これは問題#1534で追跡されています。
@avonwyssここには2つの問題があります。
コンテキストで_propertyinitializers_と入力しようとすると、 https: //github.com/Microsoft/TypeScript/pull/6118#issuecomment-216595207で説明されているいくつかの問題が発生しました。 #10570で新しいより成功したアプローチがあると思います
メソッド宣言は別のワックスのボールであり、ここで何が起こるかなど、他にも理解すべきことがいくつかあります。
declare class Base {
method(x: string): string[];
method(x: number, count: number): number[];
}
class Derived extends Base {
method(x) { // x: ???
return x;
}
}
単一署名メソッドのみがベースからパラメーター型を取得することを決定する場合があります。 しかし、これはすべての人を満足させるわけではなく、「派生クラスのメソッドに同じ_signatures_を持たせ、異なる_implementation_を提供する」というよくある問題を実際には解決しません。
その場合、 x
のタイプはstring | number
になりますが、一貫して見つけるのはかなり難しいかもしれないことを理解しています。
しかし、おそらく、 x
string | number
したくないでしょう- Derived
がBase
と同じセマンティクスを持っていると仮定すると、そうはなりません。 (3)
または('foo', 3)
で呼び出すことは合法です
ええ、私はそれを逃しました。
単一署名メソッドに制限することを提案されたようですが、例のように、「一致署名」メソッドに「簡単に」拡張できると思います。
declare class Base {
method(x: string): string[];
method(x: number, count: number): number[];
}
class Derived extends Base {
method(x, count) { // x: number, count: number
return [];
}
}
一致する基本クラスの署名のみがあるためです。
それは私たちが持っているもの(何もない)よりもはるかに良いでしょう、そして私はそれをするのは難しいとは思わないのですか? (まだこの問題の範囲外であり、おそらくより多くの作業が必要です)
@RyanCavanaugh返信ありがとうございます。 前述のように、 override
に対する私の希望は、パラメーター(および戻り値)の問題を分類することです。 しかし、オーバーロードには常にすべてのオーバーロードと互換性のある単一の「プライベート」実装が必要であるため、あなたの例に問題があるとは思いません(これが、 @ JabXからの提案が概念的に機能しない理由です)。
したがって、次のようなものがあります。
declare class Base {
method(x: string): string[];
method(x: number, count: number): number[];
// implies: method(x: string|number, count?: number): string[]|number[]
// or fancier: method<T extends string|number>(x: T, count?: number): T[]
}
class Derived extends Base {
override method(x, count?) { // may only be called like the method on Base
return [x];
}
}
このロジックはすでに存在し、オーバーライドは明らかに「プライベート」実装メソッドでのみ使用可能であり、個別のオーバーライドでは使用できません(とにかく単一のオーバーロードを明示的に実装できないのと同じように)。 だから私はここで問題を見ていません-または私は何かを逃しましたか?
オーバーライドするように設計されたメソッドには通常、オーバーロードがないため、単純な方法を取り、オーバーロードが存在する場合にパラメーターと戻り値のタイプを推測しない場合でも、完全に問題なく便利です。 したがって、オーバーロードされたメソッドをオーバーライドするときの動作がそのままであっても、 --no-implicit-any
モードで実行するとエラーが発生し、互換性のあるタイプを指定する必要があります。これは完全に優れたアプローチのように思われます。
私はおそらく何かが欠けていますが、Javascript(したがってTypescript)では、同じ名前の2つのメソッドを持つことはできません-たとえそれらが異なる署名を持っていても?
C#では、
public string method(string blah) {};
public int method(int blah) {};
しかし、ここでは、署名をどのように処理しても、同じ名前の2つの関数を使用することはできません。したがって、いずれの例も、とにかく不可能ですよね? 同じクラスの複数の拡張について話しているのでない限り...しかし、それはあなたの例が示しているものではないので、おそらくそうではありませんか? これは問題ではありませんか?
現在、継承を使用していますが、非常にイライラしています...関数についてコメントを付けて、仮想(つまり、どこかでオーバーライドしている)またはオーバーライド(つまり、この関数)であることを示す必要があります。基になる関数をオーバーライドしています)。 超迷惑で厄介です! そして安全ではありません!
@ sam-s4sはい、メソッドに対して複数の論理オーバーロードシグニチャを宣言することは可能ですが、それらはすべて同じ単一の実装になります(次に、渡されたパラメータによって「オーバーロード」が呼び出されていることを把握する必要があります)。 これは多くのJSフレームワークで頻繁に使用されます。たとえば、 $(function)
がready関数を追加するjQueryですが、 $(string)
はセレクターによってDOMを検索します。
ドキュメントについてはこちらをご覧ください: https ://www.typescriptlang.org/docs/handbook/functions.html#overloads
@avonwyssああ、そうです、宣言ですが、実装ではありません。それは理にかなっています:)ありがとう
これは長すぎて、TSにこの基本的なコンパイル時アサーションがまだない理由を理解し始めるために、この肥大化したスレッド全体を読まなければならないのは面倒です。
(a) override
キーワード、または(b)コンパイラに「同じ名前と型シグネチャを持つメソッドがスーパークラスに存在する必要がある」ことを通知する@override
jsdocアノテーションのいずれかを使用します。 ?
具体的には、@ RyanCavanaughによるhttps://github.com/Microsoft/TypeScript/issues/2000#issuecomment-224776519(2016年6月8日)のコメントは、次のステップが(コミュニティ)PRであることを示唆していますか?
class Bar extends Foo {
/**
* <strong i="12">@override</strong>
*/
public toString(): string {
// ...
}
override public toString(): string {
// ...
}
}
具体的には、 @ RyanCavanaughによる#2000(コメント)(2016年6月8日)のコメントは、次のステップが(コミュニティ)PRであることを示唆していますか?
@pcj正解
#13217でPRを始めました。 この機能に興味のある人は誰でも参加および/または提案を歓迎します。 ありがとう!
デコレータ付きのJavaと同じものを好みます
<strong i="6">@Override</strong>
public toString(): string {
// ...
}
@ Chris2011これは見覚えがあるように見えますが、これはデコレータとして、コンパイル時ではなく実行時に@Override
が評価されることを意味します。 #13217オーバーライドキーワード( public override toString()
)がレビューを進めたら、別のPRでjavadoc /** <strong i="7">@override</strong> */
を計画しています。
このスレッドの会話をフォローしようとしています。 これには静的関数が含まれますか? 完全に異なるシグネチャを持つ派生クラスで静的関数をオーバーライドすることも許可できなかった理由はわかりません。 これはES6でサポートされています。 class A { } ; A.create = function (){}
の場合はclass B extends A { } ; B.create = function (x,y) { }
で、 A.create()
を呼び出しても、 B.create()
は呼び出されず、問題が発生します。 このため(そして、ファクトリ関数と同じ関数名を型で使用する機能を作成するために)、基本静的関数のシグニチャーのオーバーライドも許可する必要があります。 それは何も壊さず、いくつかのきちんとしたことをする能力を追加します(特に、GCのフリーズを減らすためにオブジェクトのキャッシュからプルせずに常に使用される場合、「新しい」ものは本当に悪です)。 ES6では呼び出し可能なコンストラクターはサポートされていないため、型のメソッドに共通の命名規則を作成することが他の唯一のオプションです。 ただし、これを行うには、現在、派生静的関数がそれ自体とともに基本静的関数シグネチャのオーバーロードを表示する必要があります。これは、その型のみを処理する型のファクトリ関数には役立ちません。 :/それまでの間、私が持っている唯一のオプションは、フレームワークのユーザーに、派生型などのすべての基本階層の静的関数シグネチャ( SomeType.create()
など)を複製するように強制することです。 。
これが私が話している「愚かさ」についての例です(これは機能しますが、拡張可能なフレームワークで私が誇りに思うものではありません):
class A {
static create(s: string) {
var inst: A;
/* new or from cache */
inst.init(s);
}
protected init(s: string) { }
}
class B extends A {
static create(s: string);
static create(n: number);
static create(n:any) {
var inst: B;
/* new or from cache */
inst.init(n);
}
protected init(s: string);
protected init(n: number);
protected init(n: any) {
super.init(n.toString());
}
}
class C extends B {
static create(s: string)
static create(n: number)
static create(b: boolean)
static create(b: any) {
var inst: C;
/* new or from cache */
inst.init(b);
}
protected init(s: string);
protected init(n: number);
protected init(b: boolean);
protected init(b: any) {
super.init(b ? 0 : 1);
}
}
(https://goo.gl/G01Aku)
これはもっと良かったでしょう:
class A {
static create(s: string) {
var inst: A;
/* new or from cache */
inst.init(s);
}
protected init(s: string) { }
}
class B extends A {
new static create(n:number) {
var inst: B;
/* new or from cache */
inst.init(n);
}
new protected init(n: number) {
super.init(n.toString());
}
}
class C extends B {
new static create(b: boolean) {
var inst: C;
/* new or from cache */
inst.init(b);
}
new protected init(b: boolean) {
super.init(b ? 0 : 1);
}
}
@rjamesnwファクトリ関数のコントラクトを持つ静的メンバーのポリモーフィック「this」に興味があるかもしれませんが、コメント全体で主要なユースケースとして説明されています。
したがって、@ pcjが1年以上前に表現した(コメント)という気持ちをもう一度言い換えると、この機能のリクエストがどこにあるかを判断するために、ここや他の場所で非常に多くのPRやコメントを読まなければならないのは混乱します。
#13217が非常に近かったようですが、 @ DanielRosenwasserと会社によって、言語に適合しない可能性のある機能として再び撃墜され、この問題について、それを行うべきかどうかについての循環会話に再び参加したようです。 たぶん、この「デザイン時」のデコレータ(#2900)はそれを解決するでしょう、そうでないかもしれませんか? 知っておくといいですね。
数年前に投稿されたランタイムデコレータアプローチのいくつかの欠点は、私が言及していなかったものです。
唯一の注意点がクラスの初期化時にチェックが行われたことである場合、私はおそらくそれを一時的な手段として受け入れることができますが、制限が多すぎます。
率直に言って、私が最初に問題を記録してから3年間、この機能がまだそれほど物議を醸しているとは信じられません。 すべての「深刻な」オブジェクト指向言語は、このキーワード、C#、F#、C++をサポートしています...あなたはそれに名前を付けます。
javascriptが異なり、異なるアプローチが必要な理由について、一日中仮説を立てることができます。 しかし、非常に大規模なTypescriptコードベースで毎日作業する実用的な観点から、オーバーライドはコードの可読性と保守に大きな違いをもたらすと言えます。 また、派生クラスが互換性があるが微妙に異なるシグニチャを持つ基本クラスのメソッドを誤ってオーバーライドすることによるバグのクラス全体を排除します。
仮想/オーバーライド/ファイナルの適切な実装を本当に見たいです。 私はいつもそれを使っていたでしょう、そしてそれはコードをとても読みやすくしそしてエラーを起こしにくくするでしょう。 これは重要な機能だと思います。
同意しました。 比較的あいまいな/エッジケースの機能が追加されているのを見るのはイライラしますが、何かそう...基本的なものは拒否されています。 これを推進するために私たちにできることはありますか?
人に来て! これだけ多くのコメントがあり、3年以上あるのなら、なぜそれはすでに実装されていないのですか?
さあ:joy_cat:
建設的かつ具体的にしてください。 何十も必要ありませんすでにコメントしてください
でも皆さん、あなたの側にも具体的にお願いします。
明らかに、コミュニティはその機能に非常に興味を持っており、TSチームはこの機能の将来について具体的な情報を提供していません。
私は個人的に@armandnに完全に同意します。最近のリリースのTSは、このようなものが開催されている間、比較的めったに使用されない機能をもたらします。
予定がない場合は、お知らせください。 それ以外の場合は、コミュニティがどのように役立つかをお知らせください。
GitHubがロード時に表示するよりも多くのコメントがあるため、ここではタイムラインを示します。
ここで考慮していないわけではありません。 これは機能が導入されるのと同じくらい近く、同様の理由で私たち自身の機能のアイデアを拒否しました-最近の例については#24423を参照してください。
私たちは本当に意図的に言語を成長させたいと思っています。 これには時間がかかり、辛抱強く待つ必要があります。 比較すると、C++はこのスレッドの多くの人々よりも古いです; TypeScriptはまだ、大人の監督なしで家にいるほど古くはありません。 言語に何かを追加すると、それを再び取り出すことはできないため、追加するたびに、その長所と短所を慎重に比較検討する必要があります。 私は、人々が叫んでいた機能(拡張メソッド、私はあなたを見ています)が、言語を進化させ続ける能力(交差型、共用体型、条件付き型なし)を破壊したであろうという経験から話します。 GitHubのコメントがたくさんありました。 override
を追加するのが危険だと言っているのではなく、常に最大限の注意を払ってこれに取り組んでいるだけです。
今すぐoverride
の主要な問題を要約する必要がある場合:
override
と正確に一致しません(認知的負荷が増加します)strict
未満にしたいと思うフラグになりますが、大きすぎるため、現実的にはできませんでした。重大な変更(配信される値が減少します)。 そして、新しいコマンドラインフラグを暗示するものはすべて、構成スペースのもう1つの倍増であり、真剣に検討する必要があります。これは、将来の決定を行うときに精神的な予算全体を消費する前に、何かを2倍にすることしかできないためです。override
がインターフェースメンバーの実装に適用できるかどうかについてのかなり強い内部の意見の不一致(期待が一部の人々の現実と一致しないため、知覚価値が低下します)ここでの全体的な利点は、a)何かをオーバーライドしていることを示し(今日はコメントで行うことができます)、b)オートコンプリートがとにかく役立つはずだったスペルミスをキャッチし、c)新しいフラグを使用することです。キーワードを入力するのを「忘れて」、今必要な場所をキャッチします(実際のバグを見つける可能性が低い単純な機械的タスク)。 b)は非常に苛立たしいことですが、ここでも複雑さの基準を満たす必要があります。
結局のところ、JSクラスがoverride
キーワードによって非常に助けられると思うなら、それをコアランタイムに追加するためのTC39提案を支持することから始めるのが良いでしょう。
デコレータでは今日できないことは何もできません(したがって、「達成できること」という点では「新しいものではありません」)
私はこれを誤解しているかもしれませんが、デコレータが実行できない、キーワードで実行できる非常に重要なことがあります。 https://github.com/Microsoft/TypeScript/issues/2000#issuecomment-389393397でいくつか言及しました。 デコレータアプローチを使用したいのですが、非抽象メソッドはこの機能のユースケースのほんの一部にすぎないため、これらのことが可能であれば、間違いなく修正してください。
言及されていないもう1つの利点は、overrideキーワードを使用すると、基本クラスの何かを変更することがはるかに可能になることです。 名前を変更したり、何かを2つの部分に分割したりするなど、派生クラスは一致するように更新されなくても正常にコンパイルされますが、実行時に失敗する可能性があることを知っていると困難です。 また、利用可能な最終/封印/内部キーワードがないことを考えると、エクスポートされたクラスのほぼすべてがどこかで派生する可能性があり、非プライベート以外のほぼすべての変更は、オーバーライドが利用可能な場合よりもはるかにリスクが高くなります。
私の印象では、TSLintルールは、ここで最もスムーズな道であると思います。 @kungfusheepは、 https: //github.com/Microsoft/TypeScript/issues/2000#issuecomment -192502734でこのアイデアについて簡単に言及しましたが、フォローアップはありません。 この機能のTSLint実装を知っている人はいますか? そうでない場合は、機会があればハッキングを開始するでしょう。 😄
私の現在の考えは、 // @ts-ignore
や他のさまざまなコメントベースのディレクティブと同じように、コメント// @override
になるということです。 デコレータは(現在の形式では)ランタイムセマンティクスを備えており、まだステージ2であり、デフォルトで無効になっているため、個人的にはデコレータを避けたいと思います。
運が良ければ、カスタムTSLintルールは90%のソリューションであり、美学に欠けているだけです。もちろん、言語の詳細にコミットすることなく前進することができます。 タイプ認識ルール、tslint-language-service、および自動修正を使用すると、開発者の観点からは、TSLintエラーと組み込みのTSエラーの間に大きな違いはありません。 私の希望は、TSLintプラグイン(または複数の競合するプラグイン)がコミュニティにある程度の経験を与え、最良のセマンティクスについてより多くの合意に達する機会を与えることです。 次に、コアTSLintルールとして追加される可能性があり、コア言語でのoverride
キーワードの美的利点を正当化するのに十分な明快さと動機付けを提供する可能性があります。
ここで箱の外で考えているだけです。 ここには2つの異なるシナリオがあります。 メソッドはデフォルトでは仮想ではないため、C#(例として)はvirtual
とoverride
を使用します。 JavaScriptでは、クラスのすべての関数はデフォルトvirtual
です(本質的に)。 プロセスを逆にして、代わりにnooverride
型修飾子を使用する方が理にかなっていますか? もちろん、人々はそれを強制することもできますが、少なくとも、基本クラスの一部の関数には触れてはならないという慣習を推進するのに役立ちます。 繰り返しになりますが、ここでは標準の外で考えています。 ;)それはまた、重大な変化ではないかもしれません。
プロセスを逆にして、代わりに
nooverride
型修飾子を使用する方が理にかなっていますか?
私はあなたの考え方が好きです、そしてあなたが探しているのは最終的なものだと思います。
readonly
はどうですか? JSでメソッドをオーバーライドするということは、実際には、インスタンス化中(プロトタイプチェーンが適用されるとき)に親を子に置き換えることを意味すると思います。 その場合、「誰もが見ることができるが、継承によるものであれ、インスタンス化後の動的なものであれ、誰にも変更してほしくない」という意味のreadonly
メソッドを持つことは非常に理にかなっています。 すでにメンバー向けに実装されていますが、メソッドにも実装してみませんか?
すでに提案はありますか? そうでない場合は、オーバーライドする代わりに調査する価値があります...
_edit:_は、読み取り専用メンバーをオーバーライドできることが判明したため、この引数全体が折りたたまれます。
おそらくprivate
クラス関数を許可することは別のオプションですか?
編集:私は「それを最終としてマークする代わりに」考えていましたが、私は半分眠っていたに違いありません(明らかに「最終」は公開を意味しますが、オーバーライドすることはできません)、LOL; 気にしない。
@rjamesnwすでに、クラス関数をpublic、protected、privateとして定義できます。
「ファイナル」だけが解決策になるとは思いません。 問題は、その名前がすでに使用されている基本クラスから継承するクラスで誤って新しい関数を作成する可能性があることです。そうすると、理由がわからずに黙って物事を壊してしまうことになります。 エラーが発生しないことが多く、奇妙なことが起こる(または起こらない)ので、それは私に数回起こり、非常にイライラします...
したがって、実際には、仮想としてマークされていないもの(およびオーバーライドされているものがオーバーライドまたはファイナルとしてマークされている)で何かがオーバーライドされたときにコンパイラーにエラーをスローさせる新しいtsconfig.jsonエントリを見ていると思います。
override
がなければ、TypeScriptは、コンパイル時の安全性と型の安全性という[認識された]約束にユーザーを失望させていると思います。
「IDEコマンドを使用してメソッドをオーバーライドする」という引数は、コードが進化するにつれて(他の人が指摘しているように)崩壊します。
また、すべての主要な同等の言語が何らかの形でoverride
を追加したようです。
それを重大な変更にしないために、それはtslintルールである可能性があります(Javaの場合と同様)。
ところで、メジャーバージョンの言語間で重大な変更を許可しないのはなぜですか(例:2.x-> 3.x)。 行き詰まりたくないですよね?
override
の特定の詳細に問題があることが判明し、3.xでの重大な変更の微調整が必要な場合は、それで構いません。 ほとんどの人は、進化の速度と互換性のトレードオフを理解し、理解すると思います。 本当にoverride
がなければ、JavaやC#よりも実用的なものとしてTSをお勧めすることはできません...
人々が厳格になりたいのであれば、必須のoverride
の方法があるはずです(オプションのtslintルールである可能性があります)。 ただし、必須のoverride
の値ははるかに低くなります。これは、スーパークラスからメソッドを誤ってオーバーライドする可能性が、誤ってオーバーライドしない場合よりもはるかに低いように見えるためです。
本当に2015年以来開かれているこの問題は、私のTypeScriptの熱意と伝道に対する冷たい水のバケツです...
これは、grand*-parentsからのオーバーライドメソッドを処理しません。
/* Put this in a helper library somewhere */
function override(container, key, other1) {
var baseType = Object.getPrototypeOf(container);
if(typeof baseType[key] !== 'function') {
throw new Error('Method ' + key + ' of ' + container.constructor.name + ' does not override any base class method');
}
}
この問題について、GHのNo one assigned
がここにあることも悲しくて残念です。 たぶんTypeScriptフォークの時間ですか? ;)CompileTimeSafeScript .. ..
このスレッド全体は、大きな「分析による麻痺」アンチパターンのように見えます。 「80%のケースの価値を20%の作業で」実行して、実際に試して、必要に応じて3.xで微調整してみませんか?
単純で、頻繁で、非常に価値のあるユースケースは、ほとんどの人が実際に心配する必要のないいくつかのコーナーケースによって「人質」にされています。
したがって、これは、少しの助けがないわけではありませんが、デコレータを介してコンパイル時にタイプチェックすることができます。
export const override = <P extends Function>() => <K extends keyof P["prototype"]>(
target: Object,
methodName: K,
descriptor: TypedPropertyDescriptor<P["prototype"][K]>
) => {
// this is a no-op. The checking is all performed at compile-time, so runtime checks are not needed.
}
class Bar {
biz (): boolean {
return true;
}
qux (): string {
return "hi";
}
}
class Foo extends Bar {
// this is fine
@override<typeof Bar>()
biz (): boolean {
return false;
}
// error: type '() => number' is not assignable to type '() => boolean'
@override<typeof Bar>()
biz (): number {
return 5;
}
// error: argument of type '"baz"' is not assignable to parameter of type '"biz" | "qux"'
@override<typeof Bar>()
baz (): boolean {
return false;
}
}
明示的に渡さずにスーパータイプへの参照を取得できるかどうかはわかりません。 これにはわずかな実行時コストがかかりますが、チェックはすべてコンパイル時です。
@alangpierce
私の印象では、TSLintルールは、ここで最もスムーズな道であると思います。
[...]
次に、コアTSLintルールとして追加される可能性があり、コア言語のオーバーライドキーワードの美的利点を正当化するのに十分な明快さと動機付けを提供する可能性があります。
100%同意しました。 もう始めましょう!
こんにちは-計画より少し遅れていますが、デコレータを使用して実装されたtslintルールを次に示します。
https://github.com/bet365/override-linting-rule
〜1mloc TypeScriptコードベース全体でこれを数年間使用してきましたが、最近、言語サービスを使用するように更新しました。これにより、実行がはるかに高速になり、オープンソースがより実現可能になります(以前の反復では、プロジェクトのフルパスが必要でした)遺産情報を収集するため)。
私たちのユースケースにとって、それは非常に貴重でした-責任は最終的にコンパイラにあるべきであり、そのため、リンティングルールは一時的なギャップと見なされるべきであるという意見はまだあります。
ありがとう
同意します。 また、TSコンパイラを含むソリューションは、デコレータやリントルールよりもはるかに優れていると思います。
この機能のステータスは何ですか? コンパイラ機能として再検討されましたか?
したがって、これは、少しの助けがないわけではありませんが、デコレータを介してコンパイル時にタイプチェックすることができます。
いくつかの改善:
function override< Sup >( sup : { prototype : Sup } ) {
return <
Field extends keyof Sup ,
Proto extends { [ key in Field ] : Sup[ Field ] } ,
>(
proto : Proto ,
field : Field ,
descr : TypedPropertyDescriptor< Sup[ Field ] > ,
)=> {}
}
class Foo {
bar( a : number ) {
return a
}
bar2( a : number , b : number ) {
return a
}
}
class Foo2 {
@override( Foo )
bar( a : number ) {
return 1
}
@override( Foo )
bar2( a : number , b : number ) {
return 1
}
xxx() { return '777' }
}
class Foo3 extends Foo2 {
@override( Foo ) // OK
bar( a : number ) { return 5 }
@override( Foo ) // Error: less args than should
bar2( a : number ) { return 5 }
@override( Foo ) // Error: accidental override Foo2
xxx() { return '666' }
@override( Foo ) // Error: override of absent method
yyy() { return 0 }
}
OK、コンパイルエラーを防ぐオーバーライドソリューションを見つけました。
ノード読み取り可能ストリームの例:
// Interface so you will keep typings for all Readable methods/properties that are not overriden:
// Fileds that are `Omit`-ed should be overriden (with any signature you want, it do not have to be compatible with parent class)
interface ReadableObjStream<T> extends Omit<stream.Readable, 'push' | 'read'> {}
// Use extends (TYPE as any) to avoid compilation errors and override `Omit`-ted methods
class ReadableObjStream<T> extends (stream.Readable as any) {
constructor() {
super({objectMode: true}); // force object mode. You can merge it with original options
}
// Override `Omit`-ed methods with YOUR CUSTOM SIGNATURE (can be non-comatible with parent):
push(myOwnNonCompatibleSignature: T): string { /* implementation*/ };
read(options_nonCompatibleSignature: {opts: keyof T} ): string { /* implementation*/ }
}
let typedReadable = new ReadableObjMode<{myData: string}>();
typedReadable.push({something: 'else'}); // will throw compilation error as expected
typedReadable.pipe(...) // non overloaded methods typings supported as expected
このソリューションの唯一の欠点は、 super.parentMethod
を呼び出すときに型指定がないことです(ただし、 interface
ReadableObjStreamのおかげで、ReadableObjStreamのインスタンスを使用するときにすべての型付けができます。
@nin-jin@ bioballデコレータのコンパイル時チェックに貢献してくれてありがとう。
残念ながら、 protected
のメンバーでは機能しないようで、 public
でのみ機能します。
(ニンジンの遊び場の私のフォークのエラーの例
オーバーライド指定子は、C++11のキラー機能でした。
これは、リファクタリングコードを非常に助け、保護します。
私は間違いなくこれをハードルなしでTSベースのサポートに入れたいと思っています(投票!)
私たちは間違いなくこの機能の強力なサポートを示しましたが、彼らはまだこの機能をすぐに追加する予定はないようです。
別のアイデア:
class Obj {
static override<
This extends typeof Obj,
Over extends keyof InstanceType<This> = never,
>(this: This, ...overs: Over[]) {
return this as This & (
new(...a:any[])=> InstanceType<This> & Protect< Omit<InstanceType<This> , Over > >
)
}
}
class Foo extends Obj {
bar(a: number) {
return 0
}
bar2(a: number) {
return 0
}
foo = 1
}
class Foo2 extends Foo.override('bar') {
foo = 2
bar( a : number ) {
return 1
}
// Error: Class 'Foo & Protect<Pick<Foo, "bar2" | "foo">>'
// defines instance member property 'bar2',
// but extended class 'Foo2' defines it as instance member function.
bar2( a : number ) {
return 1
}
bar3( a : number ) {
return 1
}
}
declare const Protected: unique symbol
type Protect<Obj> = {
[Field in keyof Obj]:
Object extends () => any
? Obj[Field] & { [Protected]: true }
: Obj[Field]
}
ここに私の2セントを追加します。
フォームを処理し、valueChangesなどからサブスクライブを解除する必要がある典型的な角度コンポーネントがあります。 コードをあちこちで繰り返したくなかったので(ちょっと現在の状態)、AngularsのOnInitメソッドとOnDestroyメソッドを実装する「TypicalFormBaseComponent」を作成しました。 問題は、実際にそれを使用して独自のOnDestroyメソッドを追加すると(これはかなり標準的な方法です)、元のメソッドを非表示にしてシステムを破壊することです。 super.OnInitを呼び出すと修正されますが、現在、子供に強制的に実行させるメカニズムはありません。
コンストラクターでそれを行うと、super()を呼び出す必要があります...私は似たようなものを探していて、このスレッドを見つけました。正直なところ、私はちょっと困惑しています。
tsに「new」と「override」を実装することは重大な変更かもしれませんが、それを修正することは基本的にどんなコードベースでも信じられないほど簡単です(それが叫ぶところすべてに「override」を追加するだけです)。 単なる警告かもしれません。
とにかく、私が子供たちにスーパーコールを強制する他の方法はありますか? 非表示を防ぐTSLintルール、またはそのようなもの?
PS:「コメントなし」のポリシーに同意しません。 それは人々がここに例を投げることを防ぎます。 たぶんあなたは「オーバーライド」を実装しないでしょう、しかしあなたは彼らの問題を処理する何か他のものを実装するでしょう...つまり、あなたが実際にそれらを読むことができるかどうか。
@GonziHere C#やJavaなどの他の言語では、オーバーライドはスーパーメソッドを呼び出す必要があることを意味しません。実際にはそうではないことがよくあります。 コンストラクターは特別であり、通常の「仮想メソッド」ではありません。
overrideキーワードは、メソッドが基本クラスで互換性のあるシグニチャーで既に定義されている必要があることを指定するために使用され、コンパイラーはこれをアサートできます。
はい。ただし、オーバーライドが必要なため、メソッドが存在することに気付く必要があります。
@GonziHere本当に必要なのは、抽象関数を使用して抽象基本クラスを作成することです。 おそらく、より信頼できるプライベート_onInit
および_onDestroy
メソッドを作成してから、保護されたonInit
およびonDestroy
抽象関数(または、通常の関数の場合は通常の関数)を作成できます。必須ではありません)。 プライベート関数は他の関数を呼び出し、通常どおりに完了します。
@GonziHere @rjamesnw安全な方法は、実際には、 onInit
とonDestroy
が_final_であることを強制し、finalメソッドが呼び出す空の保護された抽象テンプレートメソッドを定義することです。 これにより、誰も誤ってメソッドをオーバーライドできないことが保証され、そうしようとすると(最終的なメソッドを強制する方法があると仮定して)、ユーザーはすぐに必要なものを実装する正しい方法を示します。
@ shicks 、 @ rjamesnw 、私は@GonziHereと同じ状況にあります。 これらのライフサイクルフックごとに実行したい機能があるため、基本クラスに抽象メソッドを持たせたくありません。 問題は、誰かがsuper()
を呼び出さずに子クラスにngOnInitまたはngOnDestroyを追加したときに、その基本機能が誤って置き換えられたくないということです。 override
を要求すると、開発者はそれらのメソッドが基本クラスに存在し、 super()
を呼び出す必要があるかどうかを選択する必要があることに気付くでしょう。
Javaを書いた私の経験から、 @Override
は非常に一般的であるため、ノイズになります。 多くの場合、オーバーライドされたメソッドは抽象的または空であり( super()
は呼び出されるべきではありません)、 override
の存在は、 super()
が必要であることを特に明確に示すものではありません。
Javaを書いた私の経験から、
@Override
は非常に一般的であるため、ノイズになります。 多くの場合、オーバーライドされたメソッドは抽象的または空であり(super()
は呼び出されるべきではありません)、override
の存在は、super()
が必要であることを特に明確に示すものではありません。
それは目前の主題とは何の関係もありません。 問題を回避するために、継承よりも合成を使用すると言ってもよいでしょう。
私にとって、 override
の最も有用な部分は、リファクタリング中にエラーが発生することです。
今のところ、基本クラスのメソッドの名前を変更して、それをオーバーライドするメソッドの名前を変更するのを忘れるのは簡単です。
super
を呼び出すことを忘れないでください。 場合によっては呼び出さないのも正しいです。
なぜ_オーバーライド_が重要になるのかについて誤解があると思います。 誰かに_super()_メソッドを呼び出すように強制するのではなく、メソッドが存在することを認識させ、その動作を抑制するか拡張するかを誠実に決定できるようにします。
正確さを保証するために使用するパターンは、次のように、基本クラスを非常に明示的に参照しながらParameters
とReturnType
を使用することです。
class Base {
public methodName(arg1: string, arg2: number): boolean {
return false; // base behaviour, may be stub.
}
}
class Derived extends Base {
public methodName(...args: Parameters<Base["methodName"]>): ReturnType<Base["methodName"]> {
const [meaningful, variableNames] = args;
return true; // implemented behaviour here.
}
}
この種のパターンは、 inherit
キーワードを追加するという私の提案の基礎です。 。 これにより、基本クラスへの更新が自動的に伝播され、派生クラスで無効な場合はエラーが発生します。基本クラスの名前が変更された場合も、 inherit
のアイデアでエラーが発生します。基本クラスと同一の署名を提供するだけでなく、直感的に拡張できます。
また、なぜoverride
が重要なのかについては誤解があると思いますが、「 super()
が必要かどうか」という問題についてはよくわかりません。
@lorenzodallavecchiaと問題の元の作成者が言ったことを反映するために、関数をoverride
としてマークすることは、スーパークラスがリファクタリングされたとき、特にの名前や署名がリファクタリングされたときに発生する混乱を回避するメカニズムです。オーバーライドされた関数が変更されるか、関数が削除されます。
オーバーライドされた関数のシグネチャを変更する人は、オーバーライドが存在することに気付かない可能性があることに注意してください。 (たとえばC ++で)オーバーライドがオーバーライドとして明示的にマークされていない場合、オーバーライドされた関数の名前/署名を変更してもコンパイルエラーは発生せず、単にそれらのオーバーライドがオーバーライドでなくなる原因になります。 (そして..おそらくもはや役に立たず、それらを呼び出すために使用されたコードによって呼び出されなくなり、オーバーライドを呼び出すことになっているものが現在基本実装を呼び出しているため、たくさんの新しいバグが導入されます)
C ++で実装されているoverrideキーワードは、リファクタリングの直後にコンパイルエラーが多数のオーバーライド(現在は存在しないベース実装をオーバーライドする)と手がかりを示すため、これらの問題からベース実装を変更する人を救います。おそらくオーバーライドもリファクタリングする必要があるという事実に彼らは入ります。
override
修飾子を使用することには二次的なIDEの使いやすさの利点もあります(元の作成者も言及しています)。つまり、 override
という単語を入力すると、IDEは何の可能性をたくさん示すことができます。オーバーライドできます。
override
キーワードと一緒に、有効にすると、別のメソッドをオーバーライドするすべてのメソッドがoverride
キーワードを使用することを要求するフラグを導入して、作成するケースを回避することもできます。サブクラスのメソッド、および将来的には基本クラス(サードパーティライブラリにある可能性があります)は、サブクラスで作成したメソッドと同じ名前のメソッドを作成します(サブクラスがそのメソッドをオーバーライドしたため、バグが発生する可能性があります) )。
この場合、コンパイルエラーが発生し、このメソッドにoverride
キーワードを追加する必要があることを示します(実際にはメソッドをオーバーライドしない場合でも、新しく作成されたメソッドをオーバーライドしないように名前を変更するだけです。基本クラス)、はるかに優れており、実行時のバグの可能性を回避します。
Javaを書いた私の経験から、 @ Overrideは非常に一般的であるため、ノイズになります。
何年にもわたるJavaの後で、私は上記の声明に強く反対します。 オーバーライドは、チェックされた例外のような通信方法です。 それは好きか嫌いかではなく、品質と期待についてです。
したがって、 @ lucasbasquerottoに大きな+1が加わりますが、別の観点からは、サブクラスにメソッドを導入する場合、オーバーライドするかオーバーライドしないかの明確なセマンティクスが必要です。 たとえば、メソッドをオーバーライドしたい場合は、それを明示的に伝える方法が必要です。 タイプミスや間違ったコピー&ペーストなど、実装に問題がある場合は、フィードバックを受け取りたいと思います。 または他の方法:オーバーライドすることを期待しない場合は、オーバーライドが時々発生する場合は、フィードバックも必要です。
別のJava開発者からのフィードバック...@overrideは、 Typescriptに本当に欠けている唯一の機能です。
私はJavaの経験が15年まであり、1〜2年またはTypescriptを持っているので、両方にかなり慣れています。
@overrideの禅は、インターフェイスからメソッドを削除でき、コンパイルが中断することです。
これは、新しいメソッドのタイトバインディングの逆のようなものです。 古いものを削除することができます。
インターフェイスを実装してメソッドを追加すると、コンパイルは失敗しますが、これの逆はありません。
メソッドを削除すると、デッドコードが発生します。
(おそらくそれはリンターで修正できますが)
明確にするために、 @Override
がノイズであると言ったとき、私はそれがsuper
を呼び出す必要があるというシグナルであるというコメントを具体的に参照していました。 それが提供するチェックは価値がありますが、オーバーライドされたメソッドの場合、 super
を呼び出す必要があるかどうかにかかわらず、完全に破棄されます。 オーバーライドされたメソッドのそのような大部分は_すべきではない_ので、その目的には効果がありません。
私の全体的なポイントは、サブクラスがオーバーライド可能なメソッドにコールバックすることを保証したい場合、それを行う唯一の効果的な方法は、メソッドをfinal
(特に#33446を参照)にして、 super
呼び出しなしで安全にオーバーライドできる_異なる名前の_空のテンプレートメソッド。 その不変条件を取得する他の合理的な方法はありません。 私はoverride
に完全に賛成ですが、ユーザーが私のAPIを誤ってサブクラス化することで火傷を負った人として(そして私はAPIを壊さないようにしています)、 final
に注意を向ける価値があると思いますアップスレッドで提案されたoverride
ではなく、この問題の正しい解決策としてfinal
#$。
なぜ人々がfinal
またはoverride
の一方を他方よりも提案し続けるのかについて私は少し混乱しています。 確かに、さまざまな問題を解決するので、両方が必要です。
オーバーライド
クラスを拡張している人が誤って自分の関数で関数を上書きして、知らないうちにその過程で物事を壊してしまうのを防ぎます。 overrideキーワードを設定すると、開発者は既存の関数を実際にオーバーライドしていることを知ることができ、関数の名前を変更するか、オーバーライドを使用するかを選択できます(その後、superを呼び出す必要があるかどうかを確認できます)。
最後の
クラスを拡張している人が関数を完全に上書きしないようにして、クラスの元の作成者が完全な制御を保証できるようにします。
@sam-s4s定義も必要です:-)
問題の例..
class Base {}
class Entity extends Base {
id() {
return 'BUG-123' // busisess entity id
}
}
class Base {
id() {
return '84256635572' // storage object id
}
}
class Entity extends Base {
id() {
return '12' // busisess entity id
}
}
ここで偶発的な過負荷が発生しました。
define
キーワードの場合class Base {
define id() {
return '84256635572' // storage object id
}
}
class Entity extends Base {
define id() {
return '12' // busisess entity id
}
}
エラーが発生する可能性があります:誤って再定義します。
@ nin-jinはdefine
がoverride
を使用する_not_と同じではありませんか?
@sam-s4s定義も必要です:-)
ああ、ここでキーワードdefine
について言及している人は誰もいませんが、あなたの説明からすると、C#のvirtual
という単語のような意味だと思いますか?
(また、クラスBase
とEntity
は関連していないため、例が少しわかりにくいことがわかりました。 Entity
がBase
$を拡張するという意味ですか?)
2つのシナリオがあると思います...
a) final
のないものはすべてオーバーライドできると仮定して、基本クラスを記述します。
b) virtual
のないものはすべてオーバーライドできないと想定して、基本クラスを記述します。
@ sam-s4sエンティティは、もちろんBaseを拡張します。 :-)メッセージを修正しました。 virtual
は別のことです。
override
を使用しない@lorenzodallavecchiaは、コンパイラーのdefine | override
です。 define
の使用はdefine
のみであり、コンパイラーはこれを厳密にチェックできます。
@ nin-jinあなたのモデルでは、これはoverride
キーワードを使用しないことはまだ合法であることを意味しますよね? 例えば:
class Base {
myMethod () { ... }
}
class Overridden extends Base {
// this would not fail because this is interpreted as define | override.
myMethod () { ... }
}
理想的には、 override
キーワードを使用しない限り、上記の例ではエラーが発生します。 とはいえ、オーバーライドチェックをオプトアウトするコンパイラフラグがあると思います。オプトアウトは、すべてのメソッドでoverride | define
と同じです。
@bioballはい、多くの既存のコードとの互換性のために必要です。
@ sam-s4sエンティティは、もちろんBaseを拡張します。 :-)メッセージを修正しました。
virtual
は別のことです。
定義の意味がまだわかりません...これはC#でのvirtual
の定義です。
The virtual keyword is used to modify a method, property, indexer, or event declaration and allow for it to be overridden in a derived class.
逆の意味かもしれません。オーバーライドするために関数をvirtual
としてマークする代わりに、関数をdefine
としてマークして、 override
を使用する必要があることを意味します。
(なぜdefine
?他の言語でそのキーワードを見たことがないのですか)
私は数分でこのテーマをざっと読みましたが、パラメーターと戻り値の重複指定を回避する方法としてオーバーライドを使用することを提案する人は誰もいません。 例えば:
class Animal {
move(meters:number):void {
}
}
class Snake extends Animal {
override move(meters) {
}
}
上記の例では、 move
は同じ必要なリターンタイプ( void
)を持ち、 meters
も同じタイプを持ちますが、それを指定する必要はありません。 私が働いている大企業は、Closureコンパイラのタイピングから離れて、すべてのjavascriptをtypescriptに移行しようとしています。 ただし、Closureでは、 @override
を使用するだけで、すべてのタイプがスーパークラスから推測されます。 これは、不一致の可能性を減らし、重複を減らすのに非常に役立ちます。 スーパークラスで指定されたパラメーターの型を指定しなくても、追加のパラメーターを使用してメソッドを拡張できるようにこれを実装することを想像することもできます。 例えば:
class Animal {
move(meters:number):number {
}
}
class Snake extends Animal {
override move(meters, time:number) {
}
}
私がここに来てコメントを書く動機は、typescriptにはこの機能がないため(そして私たちは長い移行中であるため)、オーバーライドされたメソッドに対してもすべての型を指定することを会社が要求していることです。 そのかなり迷惑です。
FWIWは記述が簡単ですが、パラメーター型(およびクロスファイル型推論の他のインスタンス)を省略すると、コードに不慣れな人がスーパークラスが定義されている場所を見つけて、どの型を知る必要があるため、読みやすさが妨げられmeters
。
@shicksあなたは文字通りどんなインポートされた変数またはクラスについても言うことができます。 必要になる可能性のあるすべての情報を1つのファイルに複製すると、抽象化とモジュール化の目的が損なわれます。 タイプを強制的に複製することは、DRYの原則に違反します。
最も参考になるコメント
泣き言を許してください、しかし正直なところ、あなたの議論はデフォルトですべてが公開されている言語の
public
キーワードに適用されますが、実際にはかなり世界的に分かれており、abstract
やオプションのoverride
キーワードは、開発者がより安全に感じ、ミスを減らし、時間を無駄にするのを助けるだけです。オーバーライドは、言語のタイプミスに非常に敏感な数少ない側面の1つです。これは、オーバーライドするメソッド名の入力ミスは、コンパイル時の明らかな問題ではないためです。
override
の利点は明らかです。これにより、オーバーライドする意図を述べることができます。基本メソッドが存在しない場合は、コンパイル時エラーが発生します。 すべてが型システムを歓迎します。 なぜ誰もこれを望まないのでしょうか?