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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32628864/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:05:55  来源:igfitidea点击:

Anonymous/inline interface implementation in TypeScript

javascriptinterfacetypescripttypescript1.6

提问by jezzer

I've just started with TypeScript and I'm trying to understand why the following inline object definition isn't considered valid. I have a collection of objects - their type is irrelevant (to me), but they implement the interface so that when I iterate through them I know that the interface methods will be present in each object in the collection.

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

I came across a "compiler" error when I tried to create an object with private information required to implement the required method:

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

interface Doable {
    do();
}

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

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

The compiler error message is "Argument of type '{ message: string, do: () => void; }'is not assignable to type Doable. Object literal must specify known properties, and 'message' does not exist in type Doable". Note that the same message is given if I define the object outside of the function call, i.e.

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

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

The same error occurs if I add "unexpected" methods as well:

如果我也添加“意外”方法,也会发生同样的错误:

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

I looked at the JavaScript and discovered that thiswithin the inline object definition was being scoped to the global object:

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

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

I tried searching for information on inline implementations of interfaces in TypeScript, but couldn't find anything speaking to this issue specifically.

我尝试在 TypeScript 中搜索有关内联接口实现的信息,但找不到任何与此问题相关的内容。

I can confirm that the "fixed" compiled JS works as intended:

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

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

doThatThing({
    message: 'ahoy-hoy!',
    do: function () {
        alert(this.message);
    }
});

...and that makes sense to me, because (as far as I understand) this is implicitly calling the Object constructor, so thisshould be scoped to the new Object instance.

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

It seems like the only solution is to declare each implementation as a class implementing the interface, but that feels really regressive/heavy-handed since I'm only going to have one instance of each class. If the only contract with the called function is implementing the interface, then why can't the object contain additional members?

似乎唯一的解决方案是将每个实现声明为一个实现接口的类,但这感觉真的很倒退/笨手笨脚,因为我只会有每个类的一个实例。如果与被调用函数的唯一契约是实现接口,那么为什么对象不能包含其他成员?

Sorry, this turned out longer than I intended ...in summary, I'm asking:

抱歉,结果比我预期的要长……总之,我在问:

  1. Why is that inline interface implementation ("anonymous class", as would be said in Java) considered invalid in TypeScript? Specifically, what does that compiler error mean, and what does it protect against?
  2. Why is the scope-reassignment to the global object generated in the "compiled" JavaScript?
  3. Assuming it's my error (e.g. that the compiler error is necessary for protecting against some undesirable condition), is the only solution really to explicitly declare a class in advance, like so?
  1. 为什么该内联接口实现(“匿名类”,正如在 Java 中所说)在 TypeScript 中被认为是无效的?具体来说,该编译器错误是什么意思,它可以防止什么?
  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]);
}

P.S. The compiler error only appeared when I upgraded to TS 1.6 today, although the faulty scope bug in the compiled JS occurs in both 1.6 and 1.5.

PS编译器错误只出现在我今天升级到TS 1.6的时候,虽然1.6和1.5都出现了编译JS中的错误范围bug。

Update: Fran?ois Cardinaux provided a link to this answer, which recommends using a type assertion, but this only removes the compiler error and actually causes a logic error due to improper scope:

更新:Fran?ois Cardinaux 提供了这个答案的链接,它建议使用类型断言,但这只会消除编译器错误,实际上由于范围不正确而导致逻辑错误

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);
    }
});

Looking at the compiled JS, this is incorrect:

查看编译后的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)
    }
});

采纳答案by jezzer

OK, I finally discovered the problem to question 2 - I was using the fat arrow =>to declare the object's method here:

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

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

...which "sucked" the global scope into the method. The problem is fixed using the longer syntax, like so:

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

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

So in summary:

所以总结一下:

  1. unanswered;
  2. There was a typo in my code, and I should have been using "function()" instead of "=>"; and,
  3. Type-asserting the object with the interface removes the compiler error.
  1. 未答复;
  2. 我的代码中有一个错字,我应该使用“function()”而不是“=>”;和,
  3. 使用接口对对象进行类型断言会消除编译器错误。