Typescript Function Weird Void || && Behaviour - javascript

Why is returnObjectcausing an compilation TypeError returnObject2 isn't?
Normally, void || something should return something while void && something should return void.
But in Typescript, it's the opposite that occurs.
var returnVoid = function(){};
var returnObject = function(){ //Typescript compilation TypeError.
//No best common type exists among return expressions.
if(Math.random() < 0.5)
return new Error();
return returnVoid() || null; //always return null
}
var returnObject2 = function(){ //works
if(Math.random() < 0.5)
return new Error();
return returnVoid() && null; //always return undefined
}
Note: TypeError occurs during the compilation, not in the runtime.
EDIT: I did another test. Shouldn't returnNum2 be () => number too considering (undefined || something) === something? Note: Same behaviour for void 0.
var returnNum = function(){ //() => number
return undefined || 0;
}
var returnVoid = function(){};
var returnNum2 = function(){ //() => void | number
return returnVoid() || 0;
}

It's been pointed out in comments, but I think in general you just need to understand that function(){} will return undefined, which has specified behavior for logical operators.
For undefined && somevalue, undefined will always be returned. For undefined || somevalue, somevalue will be evaluated and returned.
Here's a good reference for more information: http://www.javascriptkit.com/jsref/comparison_operators.shtml
EDIT: The question isn't about what is returned for the logical operation, but why typescript gives error TS2354: No best common type exists among return expressions. on compilation.
This does seem like an error, but may make sense in the context. If you replace the logical operators with just a call to returnVoid() only, you'll get the same error in both functions. Static typing allows the && operator to short-circuit entirely for typing, since something && null will never evaluate to a type, but something || null could depending on what the something is.
Related to this, in typescript you cannot explicitly specify null or undefined as a return type for a function.
While I understand why this may be the case, I agree it is a little odd. It might be worth checking with the folks who make Typescript and filing a bug.

TypeScript doesn't special case expressions of the form undefined || T or void || T to be T because a) you shouldn't write that code (use the comma operator!) and b) it's not safe to write this code because return value contravariance means you're not guaranteed to have a falsy value just because you have a void-returning function reference.
Consider if you wrote code like this:
type callback = (arg: any) => void;
function doSomething(x: callback) {
return x(10) || 'Hello, world!';
}
var x = [];
var add = (arg: any) => x.push(arg);
console.log(doSomething(add)); // Prints '1', not 'Hello, world!'

Related

Javascript null check performance (compare if and try/catch)

I have to get a value of a inner component. Which is better 1 or 2 when compared ONLY for performance, this method is called several times. The foo may be null depending on the runtime, but foo may have a value but bar may be null etc. It is not an error case or unexpected case that any of foo, bar, innerfoo, innerbar are null, any of them can be null at the runtime depending on the hierarchy. But innerbar can not have any value of if foo is null so there is no need to check the inner components if foo is not defined.
//1
function getInnerValue() {
if (foo && foo.bar && foo.bar.innerfoo && foo.bar.innerfoo && foo.bar.innerfoo.innerbar) {
return foo.bar.innerfoo.innerbar.x;
}
return 0;
}
//2
function getInnerValue() {
try {
return foo.bar.innerfoo.innerbar.x;
}
catch (e) {
return 0;
}
}
Anything with a try catch that ever gets triggered will incur a large performance hit.
Personally I don't find the long chain of truthy checks any less readable than a try catch block. Just longer, ugly and verbose. But really, would anyone ever have trouble reading that (or editing it)? Or better put, would you even need to read it to understand what it is doing?
In fact if anything the try catch block feels like more of a hack. I still have to think about why I'm trying to catch an exception, and it's still ugly.
In any case, we now have optional chaining, so the above doesn't even really matter anymore. I'd go with something like return x?.y?.z?.a?.b?.c?.d||0.
You can of course go with guard functions, as you've probably already noticed reading the thread you've linked. Something like lodash _.get or your own guard(a,'b','c','d').
optional chaining browser compatibility
Babel transpiled output of optional chaining
x?.y will immediately return undefined if x is null or undefined
Browser compatibility is relatively new. Check table of compatibility. You should add Babel transpilation to your pipeline.
optional chaining without transpilation:
foo = {}
function fn() {
if (foo?.bar?.innerfoo?.innerbar)
return foo.bar.innerfoo.innerbar.x
return 0
}
console.log(fn())
foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}
console.log(fn())
transpiled code:
foo = {}
function fn() {
var _foo, _foo$bar, _foo$bar$innerfoo;
if ((_foo = foo) === null || _foo === void 0 ? void 0 : (_foo$bar = _foo.bar) === null || _foo$bar === void 0 ? void 0 : (_foo$bar$innerfoo = _foo$bar.innerfoo) === null || _foo$bar$innerfoo === void 0 ? void 0 : _foo$bar$innerfoo.innerbar) return foo.bar.innerfoo.innerbar.x;
return 0;
}
console.log(fn())
foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}
console.log(fn())

Returning falsy values in javascript instead of true/false

What is the best practice in javascript when a function is supposed to return true or false? Can I return falsy values directly instead of true or false?
I found this code:
function supportsAPL(handlerInput) {
const supportedInterfaces = handlerInput.requestEnvelope.context.System.device.supportedInterfaces;
const aplInterface = supportedInterfaces['Alexa.Presentation.APL'];
return aplInterface != null && aplInterface !== undefined;
}
and simplified it to this code:
function supportsAPL(handlerInput) {
const {supportedInterfaces} = handlerInput.requestEnvelope.context.System.device;
return supportedInterfaces['Alexa.Presentation.APL'];
}
which works but I'm not sure this is proper/nice javascript. I'm looking for what an experienced javascript developer would write after finding the 1st code snippet (also looking to save lines of code).
I think the 'best practice' is to always return what the caller's are going to use it for. So, in this case, the function is named supportsAPL which seems like it should return a yes / no (true / false) to let the caller know that whatever input you gave the function supports APL or not.
You mentioned that you simplified this:
return aplInterface != null && aplInterface !== undefined;
to be this:
return supportedInterfaces['Alexa.Presentation.APL'];
In this case, we went from returning a specific true / false to returning whatever the value of supportedInterfaces['Alexa.Presentation.APL']; is. If APL is supported, you're going to get the value of supportedInterfaces['Alexa.Presentation.APL']; whereas if it's not supported, you'll likely get a falsy value of undefined
More than likely, the caller is going to be doing something like this:
if (supportsAPL(input)) {
...
}
or
const aplSupported = supportsAPL(input);
if (aplSupported) {
....
}
But, if you are only returning truthy falsy, you're going to break anybody who was expecting a boolean return. So these won't work:
if (supportsAPL(input) === true) {
...
}
or
const aplSupported = supportsAPL(input);
if (aplSupported === true) {
....
}
In my opinion, always return a boolean in these scenarios since that's the main point of the function (to determine if whatever the input is supports APL).
As #Phil mentioned,
return aplInterface != null && aplInterface !== undefined;
can be simplified to this:
return !!supportedInterfaces['Alexa.Presentation.APL']

Preserve type checking after transpiling flow/typescript

Currently, if I compile a flow/typescript file from something like the following
// #flow
function foo(x: number): string {
if (x) {
return String(x);
}
return "default string";
}
module.exports = foo;
to the following:
function foo(x ) {
if (x) {
return String(x);
}
return "default string";
}
module.exports = foo;
My question is, does there exist a way to transpile the code down to something like following?
function foo(x) {
// preserve typechecking
if (typeof x !== "number")
throw new Error(`Expected x to be of type <number> instead got <${typeof x}>!!!!`);
if (x) {
return String(x);
}
return "default string";
}
Unfortunately, the Microsoft team has decided against the use of preserving type-checking after compiling since that is against their design goals. This was an issue thread on GitHub.
Microsoft has stated in the "non-goals" section, #5, that it will not add/rely on this feature.
However, it is not too difficult to add parameter type checking when it counts.
function printMessage(message: string) {
if (typeof message != "string")
throw new Error("printMessage(message): message must be a string.");
else {
console.log(message);
}
}
I would like to point out that this isn't something javascript was intentionally designed to do. Fighting the grain can sometimes cause more headaches than it is worth.

Javascript ifDefined helper method?

Is there a helper method, or similar, in Javascript for isDefined? I just found this in a utility file I inherited:
'use strict';
var me = {
ifDefined : ifDefined,
ifDef : ifDefined,
isDefined : isDefined,
isDef : isDefined,
};
function isDefined (value) {
return (typeof value != 'undefined');
}
function ifDefined (value, defaultValue) {
return isDefined(value) ? defaultValue : value;
}
module.exports = me;
It appears the author is using it to have a shorthand method for the typeof check:
environment.u = isDef(envInfo.u, environment.u);
environment.x = isDef(envInfo.x, environment.x);
environment.s = isDef(envInfo.s, environment.s);
Is there a helper method, or similar, in Javascript for isDefined?
No, there exists no builtin function for this.
Just using value !== undefined or value != null is short enough, it didn't warrant an extra utility function. The only native "typechecking" methods I am aware of are Array.isArray, isNaN and isFinite (and their Number.is… equivalents).
There is not. If I had to do this, I wouldn't create a utility file for it. You could save just as much space in your code by using a shorthand variable and the ternary operator:
var udef = undefined;
environment.u = envInfo.u == udef ? environment.u : envInfo.u;
environment.x = envInfo.x == udef ? environment.x : envInfo.x;
environment.s = envInfo.s == udef ? environment.s : envInfo.s;
For comparison:
What this function seems to do is check if a value is undefined, and if it is, returns a default value. Otherwise it returns the original value. The name is somewhat misleading, and I don't believe there is anything directly built in to JavaScript that mimics the functionality of your ifDefined function.
If what you are looking for is to simply check if something is defined or not, using typeof is unnecessary - you can simply compare against the global undefined object:
var x;
if(x !== undefined){
console.log('this will not run');
}
else{
console.log('this will run');
}
Some extra reading, if interested:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined

JavaScript methods for setting default function variable values

I have a question regarding setting default values for variables inside functions. I know there are several posts regarding how to do this however I'm interested in this alternative method and whether anyone can explain to me the pro's/con's of using it particularly as the JSBIN compiler is flagging an error...
Line 2: void 0 === data && (data = 20); --- Expected an assignment or function call and instead saw an expression.
New/Alternative Method
var a = function(data) {
void 0 === data && ( data = 20 );
return data;
};
Current Method using typeof operator
var b = function(data) {
data = typeof data === "undefined" ? data = 30 : data;
return data;
};
I have never seen it written that way, I doubt it will take off. Developers used to use void 0 in place of undefined because undefined was once a writable attribute of the window object. With newer browsers, this has not been the case for a long while now. The only reason you'd write it like that now is to stroke your own ego and feel superior to others who may not know the meaning behind void 0.
Since void 0 is undefined. Than this:
void 0 === data && ( data = 20 );
Is a very goofy way of writing this code:
if (data === undefined)
data = 20
So executed first comparison void 0 === data and if it is true it will execute the right side of the condition which assigns 20 to the data.
The code bellow basically assigns data variable a value 30 when data is undefined. Though again this code is pretty goofy, the reason being is that you don't need to set data = 30 in the true branch, you can just return 30... this code works because when you assign a varaible a vlue the result of the assignment is the value itself so this is what happens here, operation data = 30 returns 30, but you could just write data = typeof data === "undefined" ? 30 : data;
var b = function(data) {
data = typeof data === "undefined" ? data = 30 : data;
return data;
};
The best way I think would be the following:
var b = function (data) {
data = data || 30;
}
All of the methods have various pros and cons --
Here is the method I like:
function hasOptionalParams(p1, p2 p3) {
if (arguments.length === 0) {
p1 = "default";
}
if (arguments.length === 1) {
p2 = "default";
}
if (arguments.length === 2) {
p3 = "default";
}
}
EDIT: More Details
I like this method for a number of reasons.
It is the absolute more clear for someone new to JavaScript. I work on a team and I never know who will be working on the code next and what their skill level is.
This truly does catch arguments that are not passed in, as opposed to someone who purposefully passes a null, undefined or 0
I can set multiple behaviors. I can set p2 and p3 to be different if only p1 is passed.

Categories

Resources