TypeScriptを始めたばかりで、次のインラインオブジェクト定義が有効と見なされない理由を理解しようとしています。私はオブジェクトのコレクションを持っています-それらの型は(私にとって)無関係ですが、それらを反復処理するときにコレクションの各オブジェクトにインターフェイスメソッドが存在することがわかります。
必要なメソッドを実装するために必要な個人情報を含むオブジェクトを作成しようとしたときに、「コンパイラ」エラーが発生しました。
_interface Doable {
do();
}
function doThatThing (doableThing: Doable) {
doableThing.do();
}
doThatThing({
private message: 'ahoy-hoy!', // compiler error here
do: () => {
alert(this.message);
}
});
_
コンパイラエラーメッセージは "タイプ'{ message: string, do: () => void; }'
の引数はタイプDoableに割り当てられません。オブジェクトリテラルは既知のプロパティを指定する必要があり、タイプ 'Doableに' message 'は存在しません」 。関数呼び出しの外部でオブジェクトを定義する場合、同じメッセージが表示されることに注意してください。
_var thing: Doable;
thing = {
private message: 'ahoy-hoy!', // error here
do: () => {
alert(this.message);
}
};
doThatThing(thing);
_
「予期しない」メソッドも追加すると、同じエラーが発生します。
_doThatThing({
do: () => {
alert("ahoy hoy");
},
doSecretly: () => { // compiler error here now
alert("hi there");
}
});
_
JavaScriptを見て、インラインオブジェクト定義内のthis
がグローバルオブジェクトにスコープされていることを発見しました。
_var _this = this; // wait, no, why!?
function doThatThing(doableThing) {
doableThing.do();
}
doThatThing({
message: 'ahoy-hoy!',
do: function () {
alert(_this.message); // uses global
}
});
_
TypeScriptのインターフェイスのインライン実装に関する情報を検索しようとしましたが、この問題に特化したものを見つけることができませんでした。
「修正された」コンパイル済みJSが意図したとおりに機能することを確認できます。
_function doThatThing(doableThing) {
doableThing.do();
}
doThatThing({
message: 'ahoy-hoy!',
do: function () {
alert(this.message);
}
});
_
...そして、それは私にとって理にかなっています。なぜなら、これは暗黙的にObjectコンストラクターを呼び出しているからです。したがって、this
は新しいObjectインスタンスにスコープされる必要があります。
唯一の解決策は、各実装をインターフェースを実装するクラスとして宣言することのようですが、各クラスのインスタンスは1つだけにするので、それは本当に退屈で重いです。呼び出された関数との唯一のコントラクトがインターフェイスの実装である場合、オブジェクトに追加のメンバーを含めることができないのはなぜですか?
申し訳ありませんが、これは私が意図したよりも長くなりました...要約すると、私は尋ねています:
_interface Doable {
do() : void;
}
class DoableThingA implements Doable { // would prefer to avoid this ...
private message: string = 'ahoy-hoy';
do() {
alert(this.message);
}
}
class DoableThingB implements Doable { // ... as well as this, since there will be only one instance of each
do() {
document.getElementById("example").innerHTML = 'whatever';
}
}
function doThatThing (doableThing: Doable) {
doableThing.do();
}
var things: Array<Doable>;
things = new Array<Doable>();
things.Push(new DoableThingA());
things.Push(new DoableThingB());
for (var i = 0; i < things.length; i++) {
doThatThing(things[i]);
}
_
追伸コンパイルされたJSのスコープのバグが1.6と1.5の両方で発生していますが、コンパイラエラーは今日TS 1.6にアップグレードしたときにのみ現れました。
更新:FrançoisCardinauxは この回答 へのリンクを提供しました。これは型アサーションの使用を推奨しますが、これはコンパイラエラーのみを削除し、実際に不適切なスコープによる論理エラーを引き起こします:
_interface Doable {
do();
}
function doThatThing (doableThing: Doable) {
doableThing.do();
}
doThatThing(<Doable>{ // assert that this object is a Doable
private message: 'ahoy-hoy!', // no more compiler error here
do: () => {
alert(this.message);
}
});
_
コンパイルされたJSを見ると、これは正しくありません。
_var _this = this; // very wrong, and now hidden
function doThatThing(doableThing) {
doableThing.do();
}
doThatThing({
message: 'ahoy-hoy!',
do: function () {
alert(_this.message); // s/b "this.message", which works in JS (try it)
}
});
_
OK、ついに質問2の問題を発見しました-太い矢印を使用していました=>
ここでオブジェクトのメソッドを宣言します。
doThatThing(<Doable>{
private message: 'ahoy-hoy!',
do: () => { // using fat arrow: global scope replaces new object's scope
alert(this.message);
}
});
...グローバルスコープをメソッドに「吸い込み」ます。この問題は、次のような長い構文を使用して修正されます。
doThatThing(<Doable>{
private message: 'ahoy-hoy!',
do: function() { // using "regular" anonymous function syntax, "this" meaning is preserved
alert(this.message);
}
});
要約すると: