2

Sometimes I have to check a lot of variables to exist and are defined. It looks like this one:

if (typeof store.part.items !== 'undefined' && 
  typeof domain !== 'undefined' && 
  typeof tab.partId !== 'undefined') 
{ 
  // do something useful 
}

It so boring to write that construction and takes a lot of taps to my keyboard every time, and increase a risk of error. How to short this problem?

  • 3
    You can simply use x !== undefined instead of using typeof. It's unlikely that somebody is going to overwrite undefined. – VLAZ Jun 7 at 10:11
  • 1
    Do you also have to check that store.part and store exist before you can check store.part.items? – Andy Jun 7 at 10:16
  • It's not possible to check if a variable is undeclared or not without using typeof, because typeof is an operator, not a function. Any undeclared value will throw an error if you use it as a parameter in a function call. – Hao Wu Jun 7 at 10:24
  • @HaoWu: It’s impossible for store.part.items to be undeclared, because it isn’t a name. Same with tab.partId. domain is the only one that could potentially be a declaredness check. – Ry- Jun 7 at 10:50
  • @Ry- But domain could be – Hao Wu Jun 7 at 10:51
8

You could use the rest parameter syntax to collect all the parameters passed to the function as an array. Then, use every to check if they are all non-undefined

const defined = (...values) => values.every(v => typeof v !== "undefined")

or using includes as suggested by @Ry

const defined = (...values) => !values.includes(undefined)

When calling the function, you don't have to create an array. You can simply call it with as many parameters as you like:

defined(store.part.items, domain, tab.partId)
  • 1
    Anyway, this can be simplified to !values.includes(undefined). (Or values.includes(), but that’s weird.) – Ry- Jun 7 at 10:53
  • @PauliSudarshanTerho every also shortcircuits, just like some. Try [1, 2, undefined].every(b => b.toString() === '1') This will not throw an error because toString is never called on undefined. The callback returns false for 2 and the loop ends there – adiga Jun 7 at 11:10
  • Correct @adiga! I was wrong in my thoughts here! I delete the comment to not confuse. – Pauli Sudarshan Terho Jun 7 at 11:13
  • 1
    @PauliSudarshanTerho No worries. You can always check the polyfill to verify the implementation developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… – adiga Jun 7 at 11:14
  • Yes, the polyfill show if (!testResult) { return false; } that means every does not nessecarily test every (that I first believed) – Pauli Sudarshan Terho Jun 7 at 11:19
1

A refinement to OP's self-answer using arguments for a variable argument count and a plain old for loop and early-exit for performance:

const defined = function(...arguments) {
  for (let i = 0; i < arguments.length; i++) {
    if (arguments[i] === undefined) return false;
  }
  return true;
};

// ...

if (defined(store.part.items, domain, tab.partId)) {
  // do something useful
}
if (defined(store.part.items) {
  // do something useful
}
  • 1
    function(...arguments) is a bit horrifying, to be honest :D Maybe pick another name? – Ry- Jun 7 at 10:53
  • Self answers is ok and sometimes forced by downvotes so the questioner has to find the answer. But here I saw the answer and question was made in the same minute. I saw it 13 min later. I think it is okay because only the quality of Q&A matter. – Pauli Sudarshan Terho Jun 7 at 11:56
  • Missing typeof. Is ...arguments nessecry? The function() has arguments anyway. – Pauli Sudarshan Terho Jun 7 at 12:35
1

Here is a little function that helps me very much.

const defined = function (value) {
  const resFunc = (val) => typeof val !== 'undefined';

  if (Array.isArray(value)) {
    let res = true;

    value.forEach(el => res = res && resFunc(el));

    return res;
  }

  return resFunc(value);
}

And now my code looks more happy and readable:

if (defined([store.part.items, domain, tab.partId])) {
  // do something useful
}
  • 3
    You can use .every instead of forEach rather than reassigning res, if you wish – CertainPerformance Jun 7 at 10:12
  • res = res? Why not just res? And why .forEach instead of reduce or every? – VLAZ Jun 7 at 10:12
  • There is a missing ')' in the if. Should be ... ]) ) { // do something useful – Pauli Sudarshan Terho Jun 7 at 11:51
  • Can be used to test: var store = {part: {items: []}}, domain = "http", tab = {partId: 123} – Pauli Sudarshan Terho Jun 7 at 12:38
  • thank you for your attentiveness – Maksim Tikhonov Jun 7 at 15:36
1

There's no shortcut way of doing this rather than defining your own function (which you already did). I'd like to improve your implementation of the function.

defined(exprs: array<any> | any, all?: bool)

exprs — expression(s) to check whether they're (it's) undefined or not.
all — whether expression must be the same. [useful if is exprs: array<any>]

function defined(exprs, all=true) {
  if(Array.isArray(exprs)) {
     if(all)
        return exprs.every(exp => typeof exp !== "undefined");
     else
        return exprs.some(exp => typeof exp !== "undefined");
  }
  else
     typeof exprs !== "undefined";
}
  • nice variant, I think so. But there is not need in my case to check when any of exp is not defined. But thought is quite smart – Maksim Tikhonov Jun 7 at 10:39
  • typeof exprs !== "undefined"; at the end doesn’t do anything, and this multipurpose function is kind of unpleasantly PHPesque. – Ry- Jun 7 at 10:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.