DEV Community

Discussion on: Nested Conditional Operators

 
binarypatrick profile image
BinaryPatrick

Why shouldn't you?

Thread Thread
 
link2twenty profile image
Andrew Bone • Edited

And now I'm at a PC

I would take a similar route though I'd use false as the switch argument. Remember switch statements are if(x === y ) as opposed to if(x == y ) so we can't use truthy/falsey.

In order to make it work, I'd have a simple truthy to bool converter function. It takes up a bit of space but I think it's very readable.

const toBool = a => !!a;

const validateInput = ({field1,field2,field3}) => {
  switch (false) { // if anything fails
    case toBool(field1):
      return Promise.reject('Missing field1');
    case Array.isArray(field2):
      return Promise.reject('field2 is not an array');
    case toBool(isValidType(field3)): // this may not need toBool
      return Promise.reject('field3 is invalid');
    default: // If nothing fails resolve.
      return Promise.resolve();
  }
}
Thread Thread
 
skaterdad profile image
Mike

You could also replace your custom toBool function with the built-in Boolean constructor.

Thread Thread
 
link2twenty profile image
Andrew Bone

Oh wow, I honestly didn't know that existed. It looks like it does the exact same thing as !! so would be a great replacement.

Thread Thread
 
qm3ster profile image
Mihail Malo • Edited

@skaterdad @link2twenty the Boolean function not the Boolean constructor. The difference is important because

if (Boolean('')) throw "feces" // You'll be fine
if (new Boolean('')) throw "feces" // WATCH OUT, it's a truthy Boolean {false} object!!

And not even Boolean(new Boolean('')) will save you.
You'd have to go full (new Boolean('')).valueOf() to learn the truth.

(As opposed to String(new String(true)) and Number(new Number('2')) which do manage to extract the primitive value.)

Thread Thread
 
qm3ster profile image
Mihail Malo • Edited

I leave you with this:

const a = {toString: Math.random, valueOf: ()=>false}, {log} = console
log(String(a))
log(1 / a)
log('' + a)
log(Boolean(a))

Bet you won't guess the result of the last two without evaluating :v

let _ = {valueOf: Math.random}
_+=_
Thread Thread
 
qm3ster profile image
Mihail Malo

@joelnet Why shouldn't you use switch(true){case binaryexp: return stuff}? It's the same control flow as if(){} else if(){} else if(){} or if you are always returning if() return; if() return; if() return. What could go wrong? Fallthrough?

Thread Thread
 
joelnet profile image
JavaScript Joel

When given multiple ways to write something, I always prefer writing an expression over statements.

So I wouldn't use a switch or an if when I could use a ternary

Thread Thread
 
qm3ster profile image
Mihail Malo

I bet you're all about those throw expressions, aren't you? ;)

Thread Thread
 
joelnet profile image
JavaScript Joel

Hmm. Well, I have used function expressions to throw, so this would be an improvement.

But since we're on the subject of Exception, I'll use this opportunity to rant about Exceptions.

Unless your intent is to halt the program, an Exception should not be thrown.

If your function can result in an exception, return it. This would follow a similar pattern as Promises.

const failed = Promise.resolve('success')
  .then(data => {
    return Promise.reject('Uh oh!')
  })

failed
  .then(data => console.log('data', data))
  .catch(err => console.log('err', err))

And also similar to a callback:

const myAction = (input, callback) =>
  input !== 'good' ? callback(Error('uh oh')) : callback(null, 'YES!')

myAction('bad', (err, data) => console.log({ err, data }))
myAction('good', (err, data) => console.log({ err, data }))

Example:

const myAction = (input) =>
  input !== 'good' ? { err : Error('uh oh') } : { data: 'YES!' }

myAction('bad') //=> { err: [Error: uh oh] }
myAction('good') //=> { data: 'YES!' }

The benefits are:

  • All return types are handled the same way. (with throw you have to handle an exception being returned with a try-catch).
  • Exceptions are a valid result of a function. This forces the user of the function to be aware of such return values.
Thread Thread
 
qm3ster profile image
Mihail Malo • Edited

Yeah, Result<Err, Val> is an awesome datatype, but it's not the norm in JS, unfortunately.
How great would Promise<Err, Val> = Future<Result<Err, Val>> be :)

Instead, they integrated with exceptions, which makes throwing inside an async function great, but makes awaiting a fallible promise pretty ugly.

Thread Thread
 
joelnet profile image
JavaScript Joel

That's the difficulty is going opposite of the norm. It really works out well when your entire application is designed that way, but can be odd when you inject it into an existing application.

Consistency across the application is more important. So I will do these things with a new app, but code in the same style as existing apps just for consistency.

Thread Thread
 
qm3ster profile image
Mihail Malo

Look what I found.

Thread Thread
 
joelnet profile image
JavaScript Joel

This guy knows what up ;)