I am pretty new to TypeScript.
I have created a class with some private fields. When I attempt to assign a value to one of the fields in an anonymous callback function within a class method I get the error ...
(TS) Cannot Find the name '_tokens'
I suspect that there is a scoping issue but from my understanding of JavaScript this should not be a problem. I am not sure how to fix it. Any ideas?
See .. "populateTokens()" method for error.
class SingleSignOn {
private _appTokensURL: string = "/api/IFSessionCache/Auth/";
private _tokens: string[];
/**
* Initialize an instance of the SingleSignOn class to manage the permissions for the
* application associated with the application.
*/
constructor() {
this.populateTokens();
};
/**
* Gets a list of permissions tokens associated with the currently logged on user for
* the application.
*/
private getApplicationTokens(): Q.IPromise<{}> {
return Unique.AJAX.Get(this._appTokensURL, null, ENUMS.AjaxContentTypes.JSON);
};
private populateTokens () {
this.getApplicationTokens().then(
function (data) {
_tokens = <string[]>data; // (TS) Cannot find name "_tokens"
});
};
};
You are using the wrong syntax:
this.getApplicationTokens().then(
(data) => {
this._tokens = <string[]>data; // note: turned into an arrow function and added the `this` keyword
});
note if you kept using function() ... syntax the this keyword will not point to the class instance but to the callee
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
greetings
Properties of a class do not have a scope, they live as long as the object they belong to live and everything that can access the object can access all its properties too. However properties always have to be accessed on their object, e.g. something._tokens or this._tokens inside methods. Also you have to make sure that this is what you think it is, in your case you have to use an arrow function to access the correct this inside a callback:
this.getApplicationTokens().then( (data) => {
this._tokens = data as string[];
});
I think you're just missing the this keyword from _tokens:
this._tokens = <string[]>data;
Related
I have a class:
// MyClass.ts
export class MyClass {
constructor(){
// can I get the name of the module or function which called the constructor here ??
// ex: SomeModule or testFunction
}
}
and here I create a new instance:
// SomeModule.ts
const testFunction = () => {
return new MyClass();
}
I don't want to have to pass an extra parameter to the constructor to indicate who created it. I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
That's not possible. And you shouldn't need to know anyway. If it actually is important, you should be passing it as an explicit argument.
It's mostly for logging purposes.
For that use case, you might find that a stack trace is enough (see Print current stack trace in JavaScript and related topics). A better practice though is to pass a logger object that contains the relevant context information and uses it to enrich the log calls.
I am trying to build an AWS Serverless(https://serverless.com/) function in TypeScript.
Specifically, I am defining an abstract class with a single public method that calls some private methods.
Here is my simplified TypeScript class:
export abstract class AbstractPayloadProcessor {
private preHandlePayload(payload) {
console.log('arrived in prehandle method')
}
private postHandlePayload(payload) {
console.log('arrived in posthandle method')
}
protected handleException(error) {
console.log('Exception has occurred:', error)
}
public processPayload(event) {
// this is the problematic line
this.preHandlePayload(event.payload)
.
.
.
}
}
I use webpack transpile this into ES5, which produces the following JavaScript:
var AbstractPayloadProcessor = /** #class */ (function () {
AbstractPayloadProcessor.prototype.preHandlePayload = function (payload) {
console.log('arrived in prehandle method');
};
AbstractPayloadProcessor.prototype.postHandlePayload = function (payload) {
console.log('arrived in posthandle method');
};
AbstractPayloadProcessor.prototype.handleException = function (error) {
console.log('Exception has occurred:', error);
};
AbstractPayloadProcessor.prototype.processPayload = function (event) {
// this line throws the exception
this.preHandlePayload(event.payload);
.
.
.
}());
The exact exception is:
TypeError: Cannot read property 'preHandlePayload' of undefined
So far, I have tried setting the webpack config to use ES6 and have tried to use arrow functions to declare the functions in typescript. Both produce the same exception.
The processPayload function is being called somewhere within the Serverless framework, but I do not know the implementation details. If anyone has experience with the framework and can shed some light on that, I would appreciate it.
Thanks for reading.
Wild guess
I guess that what you're doing is using someObject.processPayload and passing it as a callback to some function, or saving it somewhere as an event handler. This function in turn calls this.preHandlePayload() but it can't because not having been called properly it has its this undefined.
More info
There are few possible things that can cause this to be undefined inside of a method, and it is not important if it is private or public - because as you can see all of them end up being public after the transpilation.
One possible way is for example inside method1 if you call method2() instead of this.method2() then you will have this undefined in method2.
Another common mistake is saving a method as an event handler or using it as a callback, for example:
x.onSomething = object.method;
or
functionThatTakesCallback(object.method);
For those cases to work you either need to pass an anonymous function like:
x.onSomething = (...args) => object.method(...args);
or binding the method to that object:
x.onSomething = object.method.bind(object);
Of course, it's hard to tell what's really the problem in your case because you didn't how us how you actually call your methods and this is the calling not the definition of methods that usually puts undefined in this.
I have some classes:
class Sample{
static createSelf() {
return new this.constructor(1, 2);
}
}
class AnotherClass extends Sample {
constructor(a, b) {
this.c = a+b;
}
}
ac = AnotherClass.createSelf();
How do I do this?
This concrete example gives me SyntaxError: missing formal parameter, although in my original code (500 lines), when I have new this.constructor(), I get SyntaxError: missing ] after element list pointed at the first line (the formal parameter error is pointed at line 1, too). I know it is because of this line, as when I replace it with a normal class name, it works. There is no Array initialization close. The error cannot possibly mean:
There is an error with the array initializer syntax somewhere. Likely there is a closing bracket ("]") or a comma (",") missing.
from
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_bracket_after_list
UPDATE
original code:
class Participant {
constructor(origin, destination, number, startDate, endDate) {
...
}
static restore(save) {
const participant = new this.constructor(
new Marker(save.originLocation, this.getMarkerOptions(true, save.isDriver, save.number)).addTo(map),
new Marker(save.destinationLocation, this.getMarkerOptions(false, save.isDriver, save.number)).addTo(map),
save.number,
save.startDate,
save.endDate
);
return participant;
};
}
class Driver extends Participant {}
d = Driver.restore(saveObject);
If the error is pointing to the first line, then the syntax error is before the code you posted here.
Turns out the reason for this error is that this.constructor refers to Function which will evaluate one of the arguments passed to it as code. Since you are not passing JavaScript code to the it, you are getting a syntax error.
Example (open your browser's console):
new Function({});
However, there are two problems with the code you posted here as well.
this inside static methods
The value of this depends on how a function is called. Static methods are called as methods of the constructor function, therefore this refers to the constructor function. In your example, with AnotherClass.createSelf();, this refers to AnotherClass. Therefore this.constructor refers to Function. I don't think that's what you want. I guess you want
class Sample{
static createSelf() {
return new this(1, 2);
}
}
It looks like you thought this would refer to an instance of the class, but how could it? You haven't created one yet.
this inside constructors
When a class extends another class, you always have to call super() inside the constructor before you are accessing this:
class AnotherClass extends Sample {
constructor(a, b) {
super();
this.c = a+b;
}
}
I know that there is no REAL private method INSIDE ES6 classes. However I was playing around a little bit and discovered something good - maybe...
As I mentioned it is not possible to not expose properties of an object. But I've tried to achieve somewhat of OOP programming as I divided my classes into seperate files and then exported those classes like:
class MyClass {
constructor() {
/**
* Initialize stuff...
*/
}
myMethod() {
/**
* Do public stuff...
*/
}
}
// expose class to environment.
export default MyClass;
So I can import the class:
import MyClass from './MyClass.js';
Of course myMethod is accessible from any other file which imported the module. Sinced I was in need of variables and functions beeing accessible only by the class I've tried this:
// private variable outside of class scope but still accessible.
let possiblePrivateVariable = 'am I a private variable?';
class MyClass {
constructor() {
/**
* Initialize stuff...
*/
}
myMethod() {
// run private method.
console.log(_possiblePrivateMethod());
// show private variable.
console.log(possiblePrivateVariable);
}
}
// private function outside of class scope but still accessible.
function _possiblePrivateMethod() {
return 'am I a private method?';
}
// expose class to environment.
export default MyClass;
Now you can't access the private variable and private method:
// Import class to newFile.js and use it.
import MyClass from './MyClass.js';
const exposedClass = new MyClass();
exposedClass.possiblePrivateVariable; // -> undefined.
exposedClass._possiblePrivateMethod(); // -> undefined.
exposedClass. myMethod(); // -> logs: am I a private method?
// am I a private variable?
It is obvious that those are feeling like beeing 'private' because I am not exposing them with the word export. My question is if this method can be considered creating private variables and methods? And if the Method I've shown has any other possible leakage while running the code?
Regards,
Megajin
Sidenote: I do not need browser support since it is a NodeJS only environment. I am using NodeJS v8.1.4 and use babel in my npm start script so I can use import without any TypeError's.
I should mention as well that I'm aware that this question could be seen as a duplicate but it is not because this question is not about private properties, variables and methods inside a class but outside it.
My question is if this method can be considered creating private
variables and methods?
Yes it's an actual working solution to address the fact that ES6/7/8 do not handle privacy in classes.
And if the Method I've shown has any other possible leakage while
running the code?
Fast answer : No leak
Detailed answer:
In your private function system, what makes the function private is that in javascript the scope of a function is defined by where it's defined. In your case the file.
If you do not export the function outside the file, there is no way to access it. Like you cannot access the function in the following mainstream example :
function toto() {
const tmpToto = () => console.log('Hello');
// I can access tmpToto from here
tmpToto();
return false;
}
// I cannot access tmpToto from here
Here you get a nice explanation about scopes
More infos according to comment
How would you solve the problem that multiple class instances will
share the 'out of scope' variables?
You can use IIFE(Immediately-invoked function expression) as described by #Son JoungHo in this post.
let Name = (function() {
const _privateHello = function() {}
class Name {
constructor() {}
publicMethod() {
_privateHello();
}
}
return Name;
})();
We're in the process of porting our existing angularjs app to typescript. As this is a work in progress, we use the "any" type pretty heavily (just for clarification).
One of our larger services is being rewritten at the moment, but it heavily uses the $http service from Angular and I'm trying to figure out how to do the following:
We have a method exposed on a service that wraps a lot of functionality. Here's the basic class (dumbed down version):
class MyService implements IMyService {
static $inject = ["$http", "MyException"];
constructor(private $http: ng.IHttpService, private MyException: any) {
}
search(searchType: string, page: number): ng.IPromise<any> {
var apiCall = // Not important.
return this.executeApiCall(apiCall, this.onSearchComplete);
}
private executeApiCall(apiCall: string, onDataComplete: any): ng.IPromise<any> {
var request: any = {
method: "GET",
url: apiCall
};
return this.$http(request).then(onDataComplete);
}
private onSearchComplete(response: any): any {
if(!response.hasOwnProperty(Stuff)) {
var customError = new MyException(); // CAN'T ACCESS MyException!!
}
}
}
My problem is that since the onSearchComplete callback is passed as a delegate, I have no idea how to access MyException from it.
The this keyword isn't available in the normal sense in that function, which is the normal way I've seen you access class properties in TS.
I've trying using something similar to:
return this.executeApiCall(apiCall, this.onSearchComplete.bind(this));
But this doesn't seem to do anything, as the compiled JavaScript is no different than having the exact same statement without the bind.
My Google-foo is failing me here, and as I'm still new to TypeScript can't figure out how to accomplish the above.
Please let me know if I need to provide more details.
First of all, MyException isn't defined on onSearchComplete method scope, it is
var customError = new this.MyException();
this.onSearchComplete.bind(this) may do the work. It is JS feature and doesn't do anything on compilation time. But the best way to do this is arrow function which is intended especially to introduce lexical this inside functions:
private onSearchComplete = (response: any): any => {
if(!response.hasOwnProperty(Stuff)) {
var customError = new this.MyException();
}
}
And event handler is most obvious use case for it.