{
"path": "/posts/typescript-instance-methods",
"site": "at://did:plc:yy3apqjlms24kso7ahn7lbmb/site.standard.publication/3mova7c4nho2b",
"tags": [
"typescript"
],
"$type": "site.standard.document",
"title": "TypeScript this is what I want! (the unfortunate neglect of Instance Methods / callback functions)",
"description": "TypeScripts \"Instance Methods\" feature solves the `this` keyword issues in classes, unlike prototype methods. It suggests using a combination of the two.",
"publishedAt": "2014-04-01T00:00:00.000Z",
"textContent": "I was recently reading Jeff Walker's blog post \"Why TypeScript Isn't the Answer\". This is part of series in which Jeff goes through various compile-to-JavaScript technologies including TypeScript, CoffeeScript and Dart and explains his view of why he feels they don't quite hit the mark.\n\n\n\nAs a user (and big fan) of TypeScript I read the post with interest and picked up on one particular issue that Jeff mentions:\n\n> Classes make the unchanged behaviour of the this keyword more confusing. For example, in a class like Greeter from the TypeScript playground, the use of this is confusing:\n>\n> \n>\n> One can’t help but feel the this keyword in the methods of Greeter should always reference a Greeter instance. However, the semantics of this are unchanged from JavaScript:\n>\n> \n>\n> The above code displays “Hello, undefined” instead of the naively expected “Hello, world”.\n\nNow Jeff is quite correct in everything he says above. However, he's also missing a trick. Or rather, he's missing out on a very useful feature of TypeScript.\n\nInstance Methods to the Rescue!\n\nStill in the early days of TypeScript, the issue Jeff raises had already been identified. (And for what it's worth, this issue wasn't there by mistake - remember TypeScript is quite deliberately a \"superset of JavaScript\".) Happily with the release of TypeScript 0.9.1 a nice remedy was included in the language in the form of \"Instance Methods\".\n\nInstance Methods are lexically scoped; bound to a specific instance of a JavaScript object. i.e. These methods are \\not\\ vulnerable to the “Hello, undefined” issue Jeff raises. To quote the blog post:\n\n> We've relaxed the restrictions on field initializers to now allow 'this'. This means that classes can now contain both methods on the prototype, and callback functions on the instance. The latter are particularly useful when you want to use a member on the class as a callback function, as in the code above. This lets you mix-n-match between ‘closure’ style and ‘prototype’ style class member patterns easily.\n\nGreeter with Instance Methods\n\nSo, if we take the Greeter example, how do we apply Instance Methods to it? Well, like this:\n\nCan you tell the difference? It's subtle. That's right; the mere swapping out of () with = () => on the greet method takes us from a prototype method to an Instance Method.\n\nObservant readers will have noticed that we are using TypeScript / ES6's Arrow Function syntax. In fact with that in mind I could actually have gone super-terse if I was so inclined:\n\nBut either way, both of the above class declarations compile down to the following JavaScript:\n\nWhich differs from the pre-Instance Methods generated JavaScript:\n\nAs you can see the Instance Methods approach does \\not\\ make use of the prototype on Greeter to add the method. (As the pre-Instance Methods greet() declaration did.) Instead it creates a function directly on the created object and internally uses the _this variable inside the Instance Methods. (_this being a previously captured instance of this.)\n\nSo with Instance Methods we can repeat Jeff's experiment from earlier:\n\nBut this time round the code displays “Hello, world” and no longer “Hello, undefined”.\n\nUpdated 02/04/2014 - mixing and matching prototype and Instance Methods\n\nBart Verkoeijen made an excellent comment concerning the extra memory that Instance Methods require as opposed to prototype methods. Not everyone reads the comments and so I thought I'd add a little suffix to my post.\n\nWhat I’ve come to realise is that it comes down to problem that you’re trying to solve. Instance methods are bulletproof in terms of relying on a specific instance of this regardless of how a method is invoked. But for many of my use cases that’s overkill. Let’s take the original (prototype methods) Greeter example:\n\nAs you can see above, provided I invoke my greet method in the context of my created object then I can rely on this being what I would hope.\n\nThat being the case my general practice has not been to use exclusively Instance methods \\or\\ prototype methods. What I tend to do is start out only with prototype methods on my classes and switch them over to be an Instance method if there is an actual need to ensure context. So my TypeScript classes tend to be a combination of prototype methods and Instance methods.\n\nMore often than not the prototype methods are just fine. It tends to be where an object is interacting with some kind of presentation framework (Knockout / Angular etc) or being invoked as part of a callback (eg AJAX scenarios) where I need Instance methods.",
"canonicalUrl": "https://johnnyreilly.com/posts/typescript-instance-methods"
}