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

Posted

技术标签:

【中文标题】TypeScript 中的匿名/内联接口实现【英文标题】:Anonymous/inline interface implementation in TypeScript 【发布时间】:2015-12-14 05:40:14 【问题描述】:

我刚开始使用 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 类型中不存在‘消息’”。请注意,如果我在函数调用之外定义对象,则会给出相同的消息,即

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 实例。

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

抱歉,这比我预期的要长...总之,我在问:

    为什么内联接口实现(“匿名类”,如in Java 所说)在 TypeScript 中被视为无效?具体来说,该编译器错误是什么意思,它可以防止什么? 为什么在“已编译”的 JavaScript 中生成对全局对象的范围重新分配? 假设这是我的错误(例如,编译器错误对于防止某些不良情况是必要的),真的是像这样提前显式声明一个类的唯一解决方案吗?
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]);

附:今天升级到TS 1.6时才出现编译器错误,虽然1.6和1.5都出现编译JS的范围错误。

更新:François Cardinaux 提供了一个指向 this answer 的链接,该链接建议使用类型断言,但是这只会消除编译器错误,实际上由于范围不正确会导致逻辑错误

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

【问题讨论】:

【参考方案1】:

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

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

总之:

    未答复; 我的代码中有错字,我应该使用“function()”而不是“=>”;并且, 使用接口对对象进行类型断言可以消除编译器错误。

【讨论】:

作为未来读者的旁注:内联函数也可以在没有 function 关键字的情况下定义: message: 'ahoy-hoy!', do() alert(this.message); 。此语法只是普通函数的简写,不会像箭头函数那样绑定词法 this 应该改用do=()=&gt; ... - 即使从事件处理程序等调用,分配也确保它使用Doable 实例范围。

以上是关于TypeScript 中的匿名/内联接口实现的主要内容,如果未能解决你的问题,请参考以下文章

.net core 和 WPF 开发升讯威在线客服系统:调用百度翻译接口实现实时自动翻译

Typescript - 从接口实现匿名对象[重复]

ModelDriven & Preparable 接口

Typescript中的接口

spring 动态指定具体实现类

java Set接口