Я только начал с 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)
}
});