Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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". Note that the same message is given if I define the object outside of the function call, i.e.

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:

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.

I can confirm that the "fixed" compiled JS works as intended:

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.

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? 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?
  3. Assuming it's my error (e.g. 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]); } 

P.S. 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.

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:

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:

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)     } }); 
like image 220
jezzer Avatar asked Sep 17 '15 11:09

jezzer


People also ask

How do I create an anonymous class in TypeScript?

when you declare a class Foo in typescript you actual create an class instance of Foo & a constructor function for the class Foo . you could want to see depth in typescript. Anonymous class that ref as a constructor function like {new(... args):type} that can be created using new keyword.

Can TypeScript interface have implementation?

Interface in TypeScript can be used to define a type and also to implement it in the class.

What is anonymous function in TypeScript?

TypeScript Anonymous Functions are functions that are not bound to an identifier i.e., anonymous functions do not have name of the function. Anonymous functions are used as inline functions. These are used when the function is used only once and does not require a name. The best example is a callback function.

Can we define private method in interface TypeScript?

How to define a private property when implementing an interface in TypeScript? To define a private property when implementing an interface in TypeScript, we can add private properties into the class that implements the interface. to add the name private variable into the ModuleMenuItem class.

What is an anonymous type in typescript?

In TypeScript, we represent those through object types. As we’ve seen, they can be anonymous: or a type alias. In all three examples above, we’ve written functions that take objects that contain the property name (which must be a string) and age (which must be a number ).

What is interface in typescript?

Interface can define both the kind of key an array uses and the type of entry it contains. Index can be of type string or type number. An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces.

What is intersection type in typescript?

interface s allowed us to build up new types from other types by extending them. TypeScript provides another construct called intersection types that is mainly used to combine existing object types. An intersection type is defined using the & operator.

What is an object type in typescript?

In JavaScript, the fundamental way that we group and pass around data is through objects. In TypeScript, we represent those through object types. As we’ve seen, they can be anonymous:


1 Answers

OK, I finally discovered the problem to question 2 - I was using the fat arrow => to declare the object's method here:

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 "=>"; and,
  3. Type-asserting the object with the interface removes the compiler error.
like image 62
jezzer Avatar answered Sep 27 '22 22:09

jezzer