Подтвердить что ты не робот

Внедрение анонимного/встроенного интерфейса в TypeScript

Я только начал с 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. Литерал объекта должен указывать известные свойства, а" message "не существует в типе 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 должен быть привязан к новому экземпляру объекта.

Кажется, единственное решение - объявить каждую реализацию как класс, реализующий интерфейс, но это действительно регрессивно/тяжело, поскольку у меня будет только один экземпляр каждого класса. Если единственный контракт с вызываемой функцией реализует интерфейс, то почему объект не может содержать дополнительные члены?

Извините, это получилось дольше, чем я предполагал... в общем, я спрашиваю:

  • Почему эта встроенная реализация интерфейса ( "анонимный класс", как было сказано в 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]);
}

P.S. Ошибка компилятора появилась только при обновлении до TS 1.6 сегодня, хотя ошибка с ошибкой в ​​скомпилированном JS происходит как в версиях 1.6, так и 1.5.

Обновление: 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);
    }
});

Глядя на скомпилированный 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)
    }
});
4b9b3361

Ответ 1

ОК, я, наконец, обнаружил проблему с вопросом 2 - я использовал стрелку жира =>, чтобы объявить метод объекта здесь:

doThatThing(<Doable>{ 
    private message: 'ahoy-hoy!', 
    do: () => { // using fat arrow: global scope replaces new object 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()" вместо "= > "; и,
  • Тип-утверждение объекта с интерфейсом устраняет ошибку компилятора.