With TypeScript: unable to refer to 'this' (class) from inside a function - this

I'm learning TypeScript and have the following class:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(getCertificate)
.fail(somethingWrong);
function getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
}
As you can see on the above code, the first call to this refers to my class DetailDriver. That's good. The second call to this (inside getCertificate) refers to any. That's not what I need. I need to refer to my class DetailDriver.
How to proceed?
Thanks.

Well,
According to section 4.9.2 of the TypeScript Language Specification you should use fat arrow syntax to preserve the scoping for this.
return promise
.then(() => return.this.id;)
.fail(somethingWrong);
Then the this keyword is properly determined to be a Driver.

For reference, you could also just do:
class SomeClass {
public someMethod() {
// Do something
}
public anotherMethod() {
var that = this; // Reference the class instance
function someFunction () {
that.someMethod();
}
}
}

You could refactor to something like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(this.getCertificate.bind(this)) // <- important part
.fail(somethingWrong);
}
// new method function here
private getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
Using the function keyword anywhere in your class will make any reference to this keyword refer to that function rather than the outer class. Generally, you want to avoid defining functions inside of classes, unless you use the "fat arrow" syntax. That would look like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(() => { // <- important part
var id = this.driver.id(); // this refers to any
return ...
})
.fail(somethingWrong);
}
}

Related

NodJS how to get the Object a function belongs

some times code says it best. In below example code in Chain.add I have the function name and vars fed in to it. But I am trying to reference the object that the function is associated with. How can I do this
class Chainable {
constructor(...values) {
this._chainableConstruct={
name: this.constructor.name,
values
};
}
}
class Chain {
constructor() {
this.data=[];
}
add(func,vars) {
console.log(func.name); //returns fun
console.log(...vars); //returns test 45
console.log(func.parent); //return undefined want object t from line 28
}
}
class Test extends Chainable {
fun() {
console.log("fun");
}
}
let t=new Test();
let c=new Chain();
c.add(t.fun,["test",45]);
Out of the box, you can't. Furthermore, you can set property values of multiple objects with the same value, so the same function object might have multiple "parents".

How to return a class instance with an expression macro?

Driver code
//---------------- Main.hx ------------------------------------------------------------------
import core.reflection.ReflectionTools;
import core.SomeClass;
class Main{
public static function main(){
var s = new SomeClass();
ReflectionTools.info(s);
}
}
The macro
//---------------- ReflectionTools.hx -----------------------------------------------------
class RelectionTools {
public static macro function info(obj:Expr):Expr{
var pos = Context.currentPos();
var block = [];
var result:Dynamic;
var type:Type=Context.typeof(obj);
result=(switch(type){
case TInst(t, params):new RelectionClass(t, params);
case TEnum(t, params):new ReflectionEnum(t, params);
case TDynamic(t):new ReflectionDynamic(t);
case TFun(args, ret):new ReflectionFunction(args, ret);
case TMono(t):new ReflectionMonomorph(t);
case TLazy(f):new ReflectionLazy(f);
case TAbstract(t, params):new ReflectionAbstract(t, params);
case TType(t, params):new ReflectionTypeDefinition(t, params);
case TAnonymous(a):new ReflectionAnonymousStructure(a);
});
return $v{result};
}
}
Haxe Compiler:
Expected Expr but got core.reflection.RelectionClass (see dump/decoding_error.txt for details)
It is not a build macro (which allows returning class instances) but it is an expression macro. The best way to mimic class functionality is with abstracts.
/* Solution Description
1. a custom Json abstract with underlying type {} and with implicit
casts to underlying type. See Json.hx
2. abstracts which reflect possible Type enums with underlying custom
Json abstract and forwards. See example ReflectionPrimitive.hx
and mimic inheritance by underlying types as superclass
see example ReflectionFunction.hx
3. uses same instantiation code as original class instantiation
but now they are abstracts (see PROBLEM(The macro). Solved! */
Step 1.a custom Json abstract with underlying type {} and with implicit casts to
underlying type.
// ---------------- Json.hx ----------------------------------------------
package core.ds.json;
import haxe.Serializer;
import haxe.Unserializer;
import haxe.Json as J;
abstract Json({}) from ({}) to ({}) {
public inline function new(?data:{}){
this=data;
if(this==null){
this={};
}
}
#:arrayAccess
public inline function get(key:String):Dynamic{
if(exists(key)){
return Reflect.field(this,key);
}
return null;
}
#:arrayAccess
public inline function set(key:String, value:Dynamic):Dynamic{
Reflect.setField(this, key, value);
return value;
}
public inline function isEmpty(key:String):Bool{
return !isSet(key) || (exists(key) && ( get(key)=="" || get(key)==''|| get(key)==null || get(key)==0 ));
}
public inline function isSet(key:String):Bool{
return exists(key) && get(key)!=null;
}
public inline function exists(key:String):Bool {
return Reflect.hasField(this, key);
}
#:to
public inline function toMap():Map<String, Dynamic>{
var result:Map<String, Dynamic>=new Map<String, Dynamic>();
var fields:Array<String>=Reflect.fields(this);
for (f in fields){
result.set(f, Reflect.field(this, f));
}
return result;
}
#:to
public inline function toJsonString():String{
return J.stringify(this,null," ");
}
public inline function values():Array<Dynamic>{
var result:Array<Dynamic>=[];
var keys:Array<String>=keys();
for(k in keys){
result.push(Reflect.field(this,k));
}
return result;
}
public inline function keys():Array<String>{
return Reflect.fields(this);
}
public inline function clone():Json{
return Unserializer.run(Serializer.run(this));
}
public var length(get,never):Int;
private inline function get_length():Int{
return keys().length;
}
public inline function keyValueIterator():KeyValueIterator<String, Dynamic>{
return toMap().keyValueIterator();
}
#:from
public static function fromJsonString(json:String):Json{
return J.parse(json);
}
#:from
public static function fromMap(map:Map<String, Dynamic>):Json{
var result={};
for (k=>v in map){
Reflect.setField(result, k, v);
}
return result;
}
}
Step 2. abstracts which reflect possible Type enums with underlying custom Json abstract and forwards. See example ReflectionPrimitive.hx and mimic inheritance by underlying types as superclass see example ReflectionFunction.hx
//---------------- ReflectionPrimitive.hx ----------------------------------------------
#:forward()
abstract ReflectionPrimitive(core.ds.json.Json) from core.ds.json.Json to core.ds.json.Json{
public inline function new(nameType:String){
this=new core.ds.json.Json({data:new core.ds.json.Json(), info:new core.ds.json.Json({nameType:nameType})});
}
public var data(get, set):core.ds.json.Json;
public var info(get, set):core.ds.json.Json;
private function get_data():core.ds.json.Json {
return this["data"];
}
private function set_data(value:core.ds.json.Json):core.ds.json.Json {
this["data"]=value;
return this;
}
private function get_info():core.ds.json.Json {
return this["info"];
}
private function set_info(value:core.ds.json.Json):core.ds.json.Json {
this["info"]=value;
return this;
}
}
Mimmicking inheritance
//---------------- ReflectionFunction.hx ----------------------------------------------
#:forward(data, info, get, isEmpty, isSet, exists, toMap, toJsonString, values, keys, clone, length, keyValueIterator, fromJsonString, fromMap)
abstract ReflectionFunction(ReflectionPrimitive) from ReflectionPrimitive to ReflectionPrimitive{
public inline function new(args:Array<{t:Type, opt:Bool, name:String}>, ret:Type){
this=new ReflectionPrimitive(NameType.FUNCTION);
var newArgs=new Array<core.ds.json.Json>();
for(a in args){
newArgs.push(new core.ds.json.Json(a));
}
this.data=this.data.set("args",newArgs).set("ret", ret);
}
public var args(get, never):Array<core.ds.json.Json>;
public var ret(get,never):Type;
private function get_args():Array<core.ds.json.Json>{
return this.data.get("args");
}
private function get_ret():Type{
return this.data.get("ret");
}
}
Leave the macro untouched it will work now.
Macro is a compile time feature, not runtime feature, you can't return class instance. Instead, you have to return expression which creates a new instance (I don't have your classes, so I use here my class)
var type=Context.typeof(obj);
return (switch(type){
case TInst(t, params):macro new MyClass();//RelectionClass(t, params);
case TEnum(t, params):macro new MyClass();//ReflectionEnum(t, params);
case TDynamic(t):macro new MyClass();//ReflectionDynamic(t);
case TFun(args, ret):macro new MyClass();//ReflectionFunction(args, ret);
case TMono(t):macro new MyClass();//ReflectionMonomorph(t);
case TLazy(f):macro new MyClass();//ReflectionLazy(f);
case TAbstract(t, params):macro new MyClass();//ReflectionAbstract(t, params);
case TType(t, params):macro new MyClass();//ReflectionTypeDefinition(t, params);
case TAnonymous(a):macro new MyClass();//ReflectionAnonymousStructure(a);
});
return macro null;

How to use class functions in another class?

How can i use class instance in another class like a pointer in C++ to class instance functions?
Example:
class A {
constructor()
{
this.block = [];
}
method()
{
return this.blocks.length;
}
}
another class:
class B {
constructor(instance)
{
this.instance = instance;
}
method()
{
this.instance.method(); // here i'm getting cannot get length of undefined
}
}
If i'm trying to to like that i'm getting problems to call it
You can try this. Here, when creating B class's instance I give into it an A class's instance as argument. Then inside B we can call A instance's methods, and access its properties.
Also, as #ViaTech posted you can use static methods to access them without needing to initialize an object of the class. That is what static methods is. Refer Static Methods
class B {
constructor(instance)
{
this.instance = instance;
}
method()
{
this.instance.method();
}
}
class A {
constructor()
{
}
method()
{
console.log("A's method");
}
}
var a = new A();
var b = new B(a);
b.method(); // A's method
You can easily do this in JS by calling a static method like so:
class A {
static write(){ //static method
console.log("Called write from A");
}
}
class B {
doIt(){
A.write();
}
}
let b = new B();
b.doIt();
Option 2, you instantiate the class in the constructor of the other like so:
class A {
write(){
console.log("Non-static write() called from class A");
}
}
class B {
constructor() {
this.a = new A();
}
doIt(){
this.a.write();
}
}
let b = new B();
b.doIt();
There are a few ways:
I accidentally switched between PHP and Javascript, but the principles are the same for both)
Use static functions:
Normally, you have a this in the class. Say you have this code:
class Car {
let color;
public function setColor(newColor){ this.color = newColor;}
}
let car = new Car();
car->setColor('green')`
The setColor function's this refers to that car. You can make let anotherCar = new Car(), then when you do anotherCar->setColor('red') you only change that car, not the first one. Simplistic: You can create multiple instances.
If you do not need that, but need the class once, you can make it static. A simple way to explain would be "you have a collection of seperate functions, just put into a wrapping class (which doesn't do a lot really)". For instance, you might have some sanatizing methods:
class Sanitize {
static function makeHtmlSave(input){
return doYourMagicHere(input);
}
static function removeXssCode(input){
return doMoreMagicHere(input);
}
}
This way, you can reuse it multiple times. If you want to use it, you do Sanitize::makeHtmlSave(someCode) where you need it. There isn't a Sanitize thing, it's just a wrapper to access the frunctions inside it.
Use extend:
You can extend a class. Say you have a generic class Vehicle, which has some properties (eg a motor, numberWeels, color) and you can extend that with more specific classes:
class Vehicle {
let color;
public function setColor(newColor){ this.color = newColor}
}
class Car extends Vehicle {
let hasAirco = false;
public function hasAirco(newValue){ this.hasAirco = newValue};
}
If you do let car = new Car(), you get a Car object, that extends/enlarges/complements the Vehicle class, so you can use both its (public) functions. Internally, Car can use the functions of Vehicle too.
Just pass it
class One {
// some stuff
}
class Two{
let otherObject;
construct(givenObject){
this.otherObject = givenObject;
}
}
You can now do this let a = new One(); let b = new Two(a);. You can not use the functions of One inside Two, but you can still use a->doSomething(). This solution feels like the easiest, but it almost never is. Classes/objects are tricky stuff, but I've rarely uses this solutions. There are use cases, but often it's a bad smell indicator.

Can I overload method in module.export in node.js?

I have an app.js with this code:
var addnote = (title,body) => { /* enter code here */ }
module.exports = {addnote};
Can I add another addnotes function with different parameters to that file?
Function overloading in JavaScript does not exist like in other programming languages such as C# and Java.
What you should be looking to do is pass an object as a parameter that has properties attached and filter them out there..
You could call different functions from your little 'mapping function' just implement the logic there if it isn't big (to keep the code clear).
function foo(parameters){
var title = parameters.title;
var body = parameters.body;
if(parameters.extraProperty){
// oh we have extraProperty passed in too, run a different function?
bar(title, body, parameters.extraProperty); // ??
}
}
foo({title: 'Title', body: 'Body', extraProperty: 'This is extra...'});
If this is your own custom module, you can use the concept of function overriding, where each child class can have its own way to handle something and also have a default way to do things.
class Parent {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello ${this.name}`);
}
}
class Child1 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hey there ${this.name}. This is Child 1`);
}
}
class Child2 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hi there ${this.name}. This is Child 2`);
}
}
const o1 = new Child1('Foo')
const o2 = new Child2('Foo')
o1.greet();
o2.greet();
But if you are trying to override a function in an external module(You do not have access to that code, like a library), my suggestion is to create a wrapper and add functionality there.

PreloadJS + Typescript

I have been stuck for 4 days so it is going on SO!
Please reference the PreloadJS Definitions:
https://github.com/borisyankov/DefinitelyTyped/blob/master/preloadjs/preloadjs.d.ts
From line 26.
My question is, how do you addEventListener in typescript while also binding it to the class scope?
imagine (semi psudo):
class Test
{
private _loader:createJS.AssetLoader;
constructor()
{
this._loader = new createJS.AssetLoader();
this._loader.addEventListener(???????????);
this._loader.load();
}
onLoadComplete(???): ???
{
}
}
Using TypeScript 0.9.1.0:
class Test
{
private _loader: createJS.AssetLoader;
constructor()
{
this._loader = new createJS.AssetLoader();
this._loader.addEventListenerthis.onLoadComplete);
this._loader.load();
}
onLoadComplete = () =>
{
console.log(this.whatever);
};
}
The important part here is using the member initialization syntax (member = value) for onLoadComplete and using a fat arrow lambda expression (() => { ... }) for the function. This will correctly capture the this context in the callback.

Resources