0

I wish to check if a variable is a valid Google App Script Enum type.

function myFunc(BORDER_COLOR) {
    if (typeof BORDER_COLOR !== "Enum") {    // This does not work
        // do something for this exception
    }
    // rest of the function
}

I use typeof and instanceof to check it. But something strange happens. The Enum is an object. But it is not an instance of Object. This contradicts my understandings.

  • I suppose all primitive types (string, boolean, etc.) are non-objects.
  • I suppose all non-primitive (Array, user-defined types, etc.) types are objects.
  • (Why?)

.

Logger.log(typeof SpreadsheetApp.BorderStyle.SOLID);             // object
Logger.log(SpreadsheetApp.BorderStyle.SOLID instanceof Object);  // false <-- unexpected
Logger.log("");

var value = "text";
Logger.log(typeof value);               // string
Logger.log(value instanceof Object);    // false
Logger.log("");

var value = new String("text");
Logger.log(typeof value);               // object
Logger.log(value instanceof Object);    // true
Logger.log("");

Logger.log(Array instanceof Object);    // true
Logger.log(Object instanceof Object);   // true

Added:

var value = 123;
Logger.log(typeof value);               // number
Logger.log(TYPEOF(value));              // number
Logger.log(value instanceof Object);    // false
Logger.log("");

var value = [];
Logger.log(typeof value);               // object
Logger.log(TYPEOF(value));              // Array[0]
Logger.log(value instanceof Object);    // true
Logger.log("");

var value = {};
Logger.log(typeof value);               // object
Logger.log(TYPEOF(value));              // Object
Logger.log(value instanceof Object);    // true
Logger.log("");

function TYPEOF(value) {
  if (typeof value !== 'object')    // not an object, it is a primitive type
    return typeof value;

  var details = '';
  if (value instanceof Array) {            // if this is an object, may be this is an Array
    details += '[' + value.length + ']';
    if (value[0] instanceof Array)
      details += '[' + value[0].length + ']';
  }

  var className = value.constructor ? value.constructor.name : value.constructor;   // if this is not an Array, get the (tailor-made) constructor name
  return className + details;
}
midnite
  • 5,157
  • 7
  • 38
  • 52
  • I think they are referring to its use rather than its type. Welcome to JavaScript. – Rafael May 09 '19 at 07:11
  • But it is weird. _Something_ is an object, but it is not an instance of Object. – midnite May 09 '19 at 07:14
  • Possible duplicate of [Why does instanceof return false for some literals?](https://stackoverflow.com/questions/203739/why-does-instanceof-return-false-for-some-literals) – Rafael May 09 '19 at 07:17
  • Yeah I have read that Question also. But it does not talk about Enum. And it does not have the contradiction that `object is not an instance of Object`. – midnite May 09 '19 at 07:20
  • `Enum` is not a JavaScript type. – Rafael May 09 '19 at 07:21
  • I wish to understand JS and have it making senses. But to be target orientated first, how to verify if a parameter is an `Enum`? – midnite May 09 '19 at 07:22
  • If `Enum` is not JS native type, then it is an user-defined type. Why it is an object, but not an instance of Object? – midnite May 09 '19 at 07:23
  • Because it's a number and not an Enum. And numbers are not Objects. It is *not* a user-defined type like other languages such as Java and C++. This is the nature of JavaScript. – Rafael May 09 '19 at 07:24
  • Added some code. Numbers should be type of numbers. – midnite May 09 '19 at 07:35
  • 2
    Although I'm not sure whether this is the method you want, for example, how about using [``Object​.prototype​.toString()``](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)? In this case, ``Object.prototype.toString.call(SpreadsheetApp.BorderStyle.SOLID)`` and ``Object.prototype.toString.call(SpreadsheetApp.BorderStyle.foo)`` return ``[object JavaObject]`` and ``[object Object]``, respectively. Can this difference be used for your situation? – Tanaike May 09 '19 at 07:38
  • Not precise, but as least this is better. Thanks @Tanaike. – midnite May 09 '19 at 07:47

1 Answers1

0

Use Object.keys() to see if there's anything you can use for verification. The most valuable I found are name & ordinal. So then the easiest check will be to see if you can call name() on the Enum.

function test() {
  Logger.log(Object.keys(SpreadsheetApp.BorderStyle.DASHED)); // [name, toString, compareTo, ordinal]
  Logger.log(SpreadsheetApp.BorderStyle.DASHED.name()); // DASHED
  Logger.log("name" in SpreadsheetApp.BorderStyle.DASHED); // true

  try {  
    if ("name" in SpreadsheetApp.BorderStyle.DASHED) { // This is true
      Logger.log("SpreadsheetApp.BorderStyle.DASHED is valid enum"); // This should print
    }
  } catch (err) {}; 

  try {
    if ("name" in SpreadsheetApp.BorderStyle.NOT_VALID) { // This errors
      Logger.log("SpreadsheetApp.BorderStyle.NOT_VALID is not a valid enum"); // Will not print
    }
  } catch (err) {
    Logger.log("Errored");
  };
}

As such, you can then create a more generalized function to test enum values in the SpreadsheetApp (or adapt for other services). In fact, because the property name is being passed, we can more confidently check that it is an enum by comparing the names instead of just checking in, which also returns true for properties in the prototype chain.

/**
 * Check if the type and property are a valid Enum of the SpreadsheetApp.
 * @param {String} type
 * @param {String} property
 * @returns {Boolean}
 */
function isSpreadsheetEnum(type, property) {
  try {
    return SpreadsheetApp[type][property].name() === property;
  } catch (err) {
    return false;
  }
}

Which you could use like this:

function testEnum() {
  Logger.log(isSpreadsheetEnum("DataValidationCriteria", "DATE_ON_OR_AFTER"));
}
Diego
  • 9,261
  • 2
  • 19
  • 33
  • Thanks for answer. This seems good. It seems to me that `Object.keys()` lists out all properties (including methods) of a class. This checks if the parameter has `name` as property. This is not an unique identifier. I wonder is it because this class does not have an constructor, so we cannot retrieve the actual name of this class (BorderStyle or Enum). Just brainstorm, should I check if `OR ( all the possible values of SpreadsheetApp.BorderStyle )` ? – midnite May 09 '19 at 09:57
  • `Object.keys()` will only list those properties that are enumerable. You can "hide" properties on any object using [`Object.defineProperty()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). Note that `name()` is actually a method that returns a string. So I'm not only checking if that method exists, but also that the output of the method matches the expected value. – Diego May 09 '19 at 11:03