Nomid Posted January 21, 2016 Share Posted January 21, 2016 Hello, this is my first topic on this board. I'm coding a simple BreakOut clone in TypeScript. My app consists of a main BreakOut class and the instances for Ball, Paddle and the canvas context. BreakOut.ts /** * Created by Nomid on 20/01/2016. */ /// <reference path="GameObject.ts"/> /// <reference path="Ball.ts"/> /// <reference path="Paddle.ts"/> /// <reference path="Direction.ts"/> /// <reference path="Sprite.ts"/> /// <reference path="Key.ts"/> class BreakOut { Ball : Ball; Paddle : Paddle; private pressed_keys : { [keycode: number] : boolean }; update(time: number): void { this.context.fillStyle = "red"; this.context.fillRect(0, 0, this.context.canvas.width, this.context.canvas.height); this.Ball.update(time); this.Paddle.update(time, this.pressed_keys); } updateKeys(E: KeyboardEvent) { this.pressed_keys[E.which || E.keyCode] = !this.pressed_keys[E.which || E.keyCode]; } constructor(public context: CanvasRenderingContext2D) { this.Ball = new Ball(context); this.Paddle = new Paddle(context); this.pressed_keys = []; window.addEventListener("keypress", this.updateKeys); window.addEventListener("keyup", this.updateKeys); } } It compiles without errors, but when I run it, these errors appear: BreakOut is initialized as follows: // jQuery 2.2.0 $(document).ready(function() { var Canvas = $("<canvas/>") .attr("width", window.innerWidth) .attr("height", window.innerHeight) .appendTo("body"); window.Game = new BreakOut(Canvas[0].getContext("2d")); window.requestAnimationFrame(Game.update); }); Thank you for helping me. Quote Link to comment Share on other sites More sharing options...
mattstyles Posted January 21, 2016 Share Posted January 21, 2016 You pass the context into the constructor but never assign it to the object. A quick `this.context = context` in the constructor should solve it (not that I know much about ts mind). Oh, just saw the second error. You'll still need the context thing above but you'll need to bind those methods. I'm not sure of the specifics but it looks like TS classes are the same as regular JS classes and stuff in JS-land does not autobind methods to classes. Either of the following will work: window.addEventListener( "keypress", this.updateKeys.bind( this ) ) // or this.updateKeys = this.updateKeys.bind( this ) window.addEventListener( "keypress", this.updateKeys ) There are subtle differences in those approaches, namely the bottom one will ensure that the function is always bound whereas the 1st passes only the bound function as the event handler, if you call the function elsewhere it will not be bound. If TS supports class properties there is a third way to bind class members. --- In a different vein, using jQuery for that use is totally bonkers, I guess maybe you're using it elsewhere... Quote Link to comment Share on other sites More sharing options...
Nomid Posted January 21, 2016 Author Share Posted January 21, 2016 11 minutes ago, mattstyles said: You pass the context into the constructor but never assign it to the object. A quick `this.context = context` in the constructor should solve it (not that I know much about ts mind). I read that when specifying constructor(public member : any), member becomes a public property of the object and is auto-assigned (from typescriptlang official). For the bindings, the argument is passed as the listener works, the problem is that pressed_keys is seen as undefined in that context Quote Link to comment Share on other sites More sharing options...
chg Posted January 21, 2016 Share Posted January 21, 2016 16 minutes ago, Nomid said: I read that when specifying constructor(public member : any), member becomes a public property of the object and is auto-assigned (from typescriptlang official).For the bindings, the argument is passed as the listener works, the problem is that pressed_keys is seen as undefined in that context We can't be expected to know what you've read (also despite your capitalisation Google suggests it has not seen that exact sequence of words). You titled the thread "TypeScript: cannot set property of undefined" and detailed two errors (by screenshot) only the first of which was setting a property of a member you haven't declared which Matt reasonably suggested you should fix... As for why pressed_keys might be giving you problems, isn't it an array of length 0, what do you expect the 100th element to be??! Quote Link to comment Share on other sites More sharing options...
Nomid Posted January 21, 2016 Author Share Posted January 21, 2016 8 minutes ago, chg said: We can't be expected to know what you've read (also despite your capitalisation Google suggests it has not seen that exact sequence of words). You titled the thread "TypeScript: cannot set property of undefined" and detailed two errors (by screenshot) only the first of which was setting a property of a member you haven't declared which Matt reasonably suggested you should fix... This is what I read from here: http://www.typescriptlang.org/Handbook#classes, maybe did I misunderstand it? Anyway Matt's solution is ok, I was just wondering why the constructor didn't create the member itself, sorry for that. Quote As for why pressed_keys might be giving you problems, isn't it an array of length 0, what do you expect the 100th element to be??! So it doesn't work just as javascript does. In JavaScript you declare objects like this: var obj = {};I thought that an accessor like obj[prop] could be fine in TypeScript too. Quote Link to comment Share on other sites More sharing options...
mattstyles Posted January 21, 2016 Share Posted January 21, 2016 Quote For the bindings, the argument is passed as the listener works, the problem is that pressed_keys is seen as undefined in that context Bound or not the function will execute, it'll just be in the context of the window rather than the scope being bound to the object, which the inspector is telling you, and `pressed_keys` doesnt exist (undefined) on the window. Nomid 1 Quote Link to comment Share on other sites More sharing options...
Nomid Posted January 21, 2016 Author Share Posted January 21, 2016 (edited) 1 hour ago, mattstyles said: Bound or not the function will execute, it'll just be in the context of the window rather than the scope being bound to the object, which the inspector is telling you, and `pressed_keys` doesnt exist (undefined) on the window. Understood, so the scope in my code is switched to the window object. Thank you! window.addEventListener("keypress", this.updateKeys.bind(this)); window.addEventListener("keyup", this.updateKeys.bind(this)); Worked chg and Matt were very close to the solution of the "undefined" issue.https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript After reading this extract So, to make it work, I did: public update = (time: number) => { this.context.fillStyle = "red"; this.context.fillRect(0, 0, this.context.canvas.width, this.context.canvas.height); this.Ball.update(time); this.Paddle.update(time, this.pressed_keys); }; Thanks to all for your help!! Edited January 21, 2016 by Nomid Solution is provided Quote Link to comment Share on other sites More sharing options...
mattstyles Posted January 22, 2016 Share Posted January 22, 2016 Quote If TS supports class properties there is a third way to bind class members. Looks like it does support class properties. The actual spec can be found here. Any of the ways proposed are a solution. JS being heavily event-driven means that, in difference of most classical languages (JS is not classical, its not really a class you are defining, not in the traditional sense anyway), event handler are scoped to the element that invokes them. When JS adopted the class syntax there was a lot of discussion about whether the scope should be classical or JS-style, it was fairly unanimous that JS-style should remain. Many libraries, such as React's createClass method, goes against spec and autobinds. You're using a class property/field and an arrow function to get scope how you want it. If you look at the transpiled version you'll see whats happening (arrow functions are supported in most browsers but not all, I expect the standard TS transpilation will support older browsers, so it'll bind rather than rely on the arrow). A quick word of warning on using arrow functions like this, they mutate scope in a fairly unique way and it probably isnt how you think, more info here. Another, very brief note on class properties, you're defining a function for each new class to get the bound scope. Mostly this doesnt matter but it is worth considering. If you create 1000 instances then thats 1000 functions, normally JS creates just the 1 and references it from the prototype. Nomid and clark 2 Quote Link to comment Share on other sites More sharing options...
Nomid Posted January 22, 2016 Author Share Posted January 22, 2016 Yes, I read about that after finding the solution. Anyway, I took a look at the resulting JS, finding it interesting how it is structured. This is because, technically JS is not OOP, since prototypes only emulate the way we're used to instance objects. It was very clear, thank you clark 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.