TypeScript中的匿名/内联接口实现

jez*_*zer 35 javascript interface typescript typescript1.6

我刚刚开始使用TypeScript,我试图理解为什么以下内联对象定义不被认为是有效的.我有一个对象集合 - 它们的类型与我无关(但对我来说),但它们实现了接口,因此当我遍历它们时,我知道接口方法将出现在集合中的每个对象中.

当我尝试使用实现所需方法所需的私有信息创建对象时,我遇到了"编译器"错误:

interface Doable {
    do();
}

function doThatThing (doableThing: Doable) {
    doableThing.do();
}

doThatThing({
    private message: 'ahoy-hoy!', // compiler error here
    do: () => {
        alert(this.message);
    }
});
Run Code Online (Sandbox Code Playgroud)

编译器错误消息是"类型的参数'{ message: string, do: () => void; }'不能分配给Doable类型.对象文字必须指定已知属性,并且'Doable类型中不存在'消息'".请注意,如果我在函数调用之外定义对象,则会给出相同的消息

var thing: Doable;
thing = {
    private message: 'ahoy-hoy!', // error here
    do: () => {
        alert(this.message);
    }
};
doThatThing(thing);
Run Code Online (Sandbox Code Playgroud)

如果我添加"意外"方法,也会出现同样的错误:

doThatThing({
    do: () => {
        alert("ahoy hoy");
    },
    doSecretly: () => { // compiler error here now
        alert("hi there");
    }
});
Run Code Online (Sandbox Code Playgroud)

我查看了JavaScript并发现this内联对象定义中的范围是全局对象:

var _this = this; // wait, no, why!?
function doThatThing(doableThing) {
    doableThing.do();
}
doThatThing({
    message: 'ahoy-hoy!',
    do: function () {
        alert(_this.message); // uses global 
    }
});
Run Code Online (Sandbox Code Playgroud)

我尝试在TypeScript中搜索有关接口的内联实现的信息,但没有找到任何具体说明此问题的内容.

我可以确认"固定"编译的JS按预期工作:

function doThatThing(doableThing) {
    doableThing.do();
}

doThatThing({
    message: 'ahoy-hoy!',
    do: function () {
        alert(this.message);
    }
});
Run Code Online (Sandbox Code Playgroud)

...这对我来说很有意义,因为(据我所知)这是隐式调用Object构造函数,所以this应该限定为新的Object实例.

似乎唯一的解决方案是将每个实现声明为实现接口的类,但是由于我只会有一个每个类的一个实例,因此感觉非常退步/苛刻.如果与被调用函数的唯一契约是实现接口,那么为什么对象不能包含其他成员?

对不起,这比我预想的要长...总结一下,我问:

  1. 为什么在TypeScript中认为内联接口实现("匿名类",如Java中所说)无效?具体来说,该编译器错误意味着什么,它有什么保护作用?
  2. 为什么在"已编译"的JavaScript中生成的全局对象的范围重新分配?
  3. 假设这是我的错误(例如,编译器错误是防止某些不良条件所必需的),是否真的是提前明确声明一个类的唯一解决方案,如此?
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]);
}
Run Code Online (Sandbox Code Playgroud)

PS编译器错误仅在我今天升级到TS 1.6时出现,尽管编译的JS中的错误范围错误发生在1.6和1.5中.

更新: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);
    }
});
Run Code Online (Sandbox Code Playgroud)

看看编译好的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)
    }
});
Run Code Online (Sandbox Code Playgroud)

jez*_*zer 15

好吧,我终于发现了问题2的问题 - 我在这里使用胖箭头=>来声明对象的方法:

doThatThing(<Doable>{ 
    private message: 'ahoy-hoy!', 
    do: () => { // using fat arrow: global scope replaces new object's scope
        alert(this.message);
    }
});
Run Code Online (Sandbox Code Playgroud)

......将全局范围"吸"到方法中.使用较长的语法修复了该问题,如下所示:

doThatThing(<Doable>{
    private message: 'ahoy-hoy!',
    do: function() { // using "regular" anonymous function syntax, "this" meaning is preserved
        alert(this.message);
    }
});
Run Code Online (Sandbox Code Playgroud)

总结如下:

  1. 悬而未决;
  2. 我的代码中有一个拼写错误,我应该使用"function()"而不是"=>"; 和,
  3. 使用接口声明对象的类型 - 删除编译器错误.