简体   繁体   English

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

[英]Anonymous/inline interface implementation in TypeScript

I've just started with TypeScript and I'm trying to understand why the following inline object definition isn't considered valid. 我刚刚开始使用TypeScript,我试图理解为什么以下内联对象定义不被认为是有效的。 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. 我有一个对象集合 - 它们的类型与我无关(但对我而言),但它们实现了接口,因此当我遍历它们时,我知道接口方法将出现在集合中的每个对象中。

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" . 编译器错误消息是“类型'{ message: string, do: () => void; }'参数'{ message: string, do: () => void; }'不能分配给Doable类型。对象文字必须指定已知属性,并且“Doable”类型中不存在“message” Note that the same message is given if I define the object outside of the function call, ie 请注意,如果我在函数调用之外定义对象,则会给出相同的消息

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 this within 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 this should 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? 为什么在TypeScript中认为内联接口实现(“匿名类”,如Java中所说)无效? 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? 为什么在“已编译”的JavaScript中生成的全局对象的范围重新分配?
  3. Assuming it's my error (eg 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? 假设这是我的错误(例如,编译器错误是防止某些不良条件所必需的),是否真的是提前明确声明一个类的唯一解决方案,如此?
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]);
}

PS 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时出现,尽管编译的JS中的错误范围错误发生在1.6和1.5中。

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

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

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 "=>"; 我的代码中有一个拼写错误,我应该使用“function()”而不是“=>”; and, 和,
  3. Type-asserting the object with the interface removes the compiler error. 使用接口声明对象的类型 - 删除编译器错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM