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

当我试图创建一个包含实现所需方法所需的私有信息的对象时,我遇到了一个"编译器"错误:

interface Doable {
    do();
}

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

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

编译器错误消息是"Argument of type 100 is not assignable to type Doable. Object literal must specify known properties, and 'message' does not exist in type 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);
    }
});

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

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

抱歉,结果比我预想的要长...总之,我想问:

  1. 为什么在TypeScript中,内联接口实现("匿名类",可以说是in 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]);
}

另外,编译器错误直到今天我升级到TS 1.6时才出现,尽管编译后的JS中的错误范围错误出现在1.6和1.5中.

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

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

推荐答案

好的,我终于发现了问题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);
    }
});

总之:

  1. 未获答复的
  2. There was a typo in my code, and I should have been using "function()" instead of "=>"; and,
  3. 通过接口断言对象的类型将删除编译器错误.

Typescript相关问答推荐

为什么当类类型与方法参数类型不兼容时,该函数可以接受类类型

具有映射返回类型的通用设置实用程序

TS 2339:属性切片不存在于类型DeliverableSignal产品[]上>'

如何用不同的键值初始化角react 形式

TypeScript类型不匹配:在返回类型中处理已经声明的Promise Wrapper

TypeScript Page Routing在单击时不呈现子页面(React Router v6)

如何访问Content UI的DatePicker/中的sx props of year picker?'<>

material 表不渲染任何数据

类型脚本基于数组作为泛型参数展开类型

为什么我的条件类型不能像我预期的那样工作?

表单嵌套太深

如何将定义模型(在Vue 3.4中)用于输入以外的其他用途

刷新页面时,TypeScrip Redux丢失状态

有没有可能创建一种类型,强制在TypeScrip中返回`tyPeof`语句?

如何使这种一对一关系在旧表上起作用?

为什么TS2339;TS2339;TS2339:类型A上不存在属性a?

KeyOf关键字在特定示例中是如何工作的

递归JSON类型脚本不接受 node 值中的变量

在Typescript 无法正常工作的 Three.js 中更新动画中的 GLSL 统一变量

使用本机 Promise 覆盖 WinJS Promise 作为 Chrome 扩展内容脚本?