I wanted to check whether a variable in an Apps Script was a String, but found that instanceof wasn't returning true when the variable was in fact a string. The following test:
function test_instanceof() {
var a = "a";
Logger.log('"a" is ' + ((a instanceof String) ? '' : 'not ') + 'a String');
var b = String("b");
Logger.log('String("b") is ' + ((b instanceof String) ? '' : 'not ') + 'a String');
}
Logs these two messages:
"a" is not a String
String("b") is not a String
The docs aren't clear on the subset of ECMAScript that is supported, though apparently instanceof is a valid operator and String is a valid type, judging from the fact that
the code executed without an exception.
What is the appropriate way to check the type of a variable?
It's standard EcmaScript 3.
Your code is doing what JavaScript expects: see here for the equivalent JavaScript running in your browser.
Instanceof checks for a matching constructor in the prototype chain. That's good for objects created via 'new' but not very helpful for strings. What you actually want for String is typeof, as shown in this example in your browser or the equivalent Apps Script code:
function test_instanceof() {
var a = "a";
Logger.log('"a" is ' + ((typeof a == 'string') ? '' : 'not ') + 'a String');
var b = String("b");
Logger.log('String("b") is ' + ((typeof b == 'string') ? '' : 'not ') + 'a String');
}
Related
I have some code takes a string of raw Markdown stored in 'markdownString' and converts it to HTML with the marked.js library for display on the web browser. I am trying to grab all plain text values from the page and to store it in an array of strings by using a TreeWalker and then iterating over the nodes.
I'm getting the error:
Type 'Node | null' is not assignable to type 'Node'.
Type 'null' is not assignable to type 'Node'.
When trying to use
currentNode = walker.nextNode();
const htmlString = marked(markdownString);
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, 'text/html');
const walker = document.createTreeWalker(doc, NodeFilter.SHOW_TEXT);
const textList: string[] = [];
let currentNode = walker.currentNode;
while (currentNode != null) {
if (currentNode.textContent != null) {
textList.push(currentNode.textContent);
}
if (walker.nextNode() != null) {
currentNode = walker.nextNode();
} else {
break;
}
}
I'm not sure why TypeScript is complaining about not being able to assign 'null' to 'currentNode' when I already have a check in place if nextNode() is null before entering the block to assign 'currentNode = walker.nextNode()'
Because each call to nextNode() might return a different value.
To use the type guard (aka "narrowing"... your if statement) you'll need to use an intermediate variable that TypeScript can make reasonable assumptions about:
const nextNode = walker.nextNode();
if (nextNode != null) {
currentNode = nextNode;
} else {
break;
}
Calling nextNode() twice was probably resulting in an undiscovered bug anyway as it was advancing your walker and thus skipping every other node.
I am migrating the database of my node.js/typescript project from Oracle to MYSQL.
My queries/dml in Oracle are all bind in this style
conn.execute('select date, name from table
where id = :ID and field = :VAR',
{ID: variable1, VAR: variable2});
When using MYSQL I found this:
connection.query('select date, name from table
where id = ? and field = ?',
[variable1, variable2]);
The second approach is worse for me because of following reasons:
i- I would to rewrite a lot of sql calls in my code
ii- I think the first approach is much more reliable, as you are not concerning of having unpredictable results due to changing in SQL
Although I found some mention to the first style here, it couldn't make it work
Any tips?
As I didn't find anything ready that could solve the issue, I tried to solve the problem. Maybe this could be helpful.
first, this code gets an Oracle bind interface type like {ID: 105, DEPT: 'MKT'} and a query like 'select * from emp where id = :ID and deptName = :DEPT' and translates them to [105,'MKT'] and 'select * from emp where id = ? and deptName = ?'
here is the code
const endBindCharacters: string = ' )=';
function prepareSQL(sql: string, binders: Object = null, valueArray: TBindArray): string {
let ich: number = 0;
let bindVariable: string;
if (! binders) {
if (sql.indexOf(':') > 0) {
throw new CustomError(errorCodes.connection.sqlBoundWithNoBinders,
'No binders {} in a bound SQL ' + sql);
};
return sql;
};
while ((ich = sql.indexOf(':')) > 0) {
bindVariable = '';
while (!endBindCharacters.includes(sql[++ich]) && ich < sql.length) {
bindVariable += sql[ich];
};
if (binders[bindVariable]) {
valueArray.push(binders[bindVariable]);
} else {
throw new CustomError(errorCodes.connection.bindVariableNotInBinders, ' Bind variable ' + bindVariable +
' não encontradada no binders {} da expressão:\n' + sql)
};
sql = sql.replace(':' + bindVariable, ' ? ');
};
return sql;
};
This is the wrapper. It will get a Promise from the callback.
export async function executeSQL (conn: TConnection, sql: string,
binders: Object = {}): Promise<TReturn> {
let bindArray: TBindArray = [];
sql = prepareSQL(sql, binders, bindArray);
console.log(sql, binders, bindArray);
return new Promise<TReturn>(function(resolve, reject) {
conn.query(sql, bindArray , function(err: db.IError, results: TReturn) {
if(err) {reject(err)}
else {resolve(results)};
});
});
};
i just noticed , that this function (using default params) doesn't cause error on compilation.
function buildAddress(address1 = 'N/A', address2: string) {
displayAddress( address1 +' '+ address2);
}
but this function (using optional params) does.
function buildAddress(address1?: string, address2: string) {
displayAddress( address1 +' '+ address2);
}
why is it so ?
i'm really surprised of this behavior, is that normal ? does it have any benefit ? is this a feature or a bug ?
Have you tried to use the first version without passing the first argument?
function buildAddress(address1: string = 'N/A', address2: string) {
console.log(address1, address2);
}
buildAddress("address2");
Results in:
Supplied parameters do not match any signature of call target
If you put the default value for the 2nd parameter though:
function buildAddress(address1: string , address2: string = 'N/A') {
console.log(address1, address2);
}
It works.
Adding default value for the first parameter only helps you in case you pass undefined:
buildAddress(undefined, "address2");
As it compiles to:
function buildAddress(address1, address2) {
if (address1 === void 0) { address1 = 'N/A'; }
console.log(address1, address2);
}
So in reality if you're doing this then the first parameter isn't optional at all, you must pass a value, and you only get the default value if you pass undefined.
But the compiler won't complain about the function signature because the first parameter has a value for sure, but in your 2nd function, since the first param is optional the compiler complains.
Edit
This behvior can be used to safe guard against undefined values, for example:
function buildAddress(address1 = 'N/A', address2: string) {
displayAddress(address1 + ' ' + address2);
}
function getAddress1(): string {
// logic here, might return undefined
}
buildAddress(getAddress1(), "address 2");
I'm not sure if this is by design or just a byproduct, but it's useful in certain cases.
I currently have some code from here (https://github.com/jmhnilbog/Nilbog-Lib-AS2/blob/master/mx/mx/remoting/NetServiceProxy.as) which converts a function into a function. This code is shown below:
private var _allowRes:Boolean= false;
function __resolve( methodName:String ):Function {
if( _allowRes ) {
var f = function() :Object {
// did the user give a default client when he created this NetServiceProxy?
if (this.client != null) {
// Yes. Let's create a responder object.
arguments.unshift(new NetServiceProxyResponder(this, methodName));
}
else {
if (typeof(arguments[0].onResult) != "function") {
mx.remoting.NetServices.trace("NetServices", "warning", 3, "There is no defaultResponder, and no responder was given in call to " + methodName);
arguments.unshift(new NetServiceProxyResponder(this, methodName));
}
}
if(typeof(this.serviceName) == "function")
this.serviceName = this.servicename;
arguments.unshift(this.serviceName + "." + methodName);
return( this.nc.call.apply(this.nc, arguments));
};
return f;
}
else {
return null;
}
}
Basically what the code is designed to do is return a new function (returned as f) which performs the correct server operates. However, if I try and use this syntax in AS3, I get the following two errors:
Error: Syntax error: expecting semicolon before colon.
Error: Syntax error: else is unexpected.
How would I go about doing this? I know this is someone else's code, but I am trying to get the old AS1/2 mx.remoting functionality working in AS3. Cheers.
I'm using Node.js and Underscore.js. I can't determine whether to escape JSON data on server side or client side. For underscore doesn't auto escape interpolated values with syntax <%= someValue %> but with <%- someValue %>, which is in the contrast to EJS and may causes confusion. There was a issue on GitHub and also a commit of auto-escape version. But a comment beneath the issue said:
I'm of the general philosophy that escaping should be done closer to
your data than in the templating language
So, any suggestion that when to do HTML escape to AJAX data is better? Here's the server side helper function I have been using:
var htmlEscape = function(html){
return String(html)
.replace(/&(?!\w+;)/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
};
var xss = function(obj) {
if (obj instanceof Array) {
for (var i = 0; i < obj.length; i++) {
obj[i] = xss(obj[i]);
}
} else {
for(var key in obj) {
// key != '_id' for mongoose doc
if(obj[key] instanceof Object && !(obj[key] instanceof String)
&& !(obj[key] instanceof Function) && key != '_id') {
obj[key] = xss(obj[key]);
} else if (obj[key] instanceof String || typeof(obj[key]) == "string") {
obj[key] = htmlEscape(obj[key]);
} else {
obj[key] = obj[key];
}
}
}
return obj;
};
Then call it whenever return a JSON:
res.json(xss(someData));
It is always better to perform sanitization/escape operations on the server since anyone can mess with your client side code and send the data any way they want.
There is a great node.js module, node-validator, which has an xss() function as well as a bunch other functions to validate/sanitize your data.