The right way to check arguments data types

Hi everyone! I’m write the check of arguments data types inside function body. But I want to create the common function for check of data types, which will be used in several other functions.
It’s my constructor of “Cat” object:

function Cat(name, age, owner) {
	
	if (!checkArgsDataTypes(arguments.callee, ["string", "number", "Man"])) {
		throw new Error("Incorrect data type of arguments");
	}
	
	/* Continue function body if data types is correct */
	
}

Here creating of the “Cat” object:
var my_cat = new Cat("Tom", 2, new Man());

And this my variant of the common check-function:

function checkArgsDataTypes(func_link, types_array) {
	
	var i;
	var current_data_type;
	
	if (typeof func_link != "function") {
		throw new Error("the first argument must be a function");
	}
	
	if (types_array instanceof Array) {
		
		if (func_link.arguments.length != types_array.length) {
			throw new Error("not matching count of arguments and count of array-type items");
		}
		
		if (types_array.length > 0) {
			for (i = 0; i < types_array.length; i ++) {
				if (typeof types_array[i] != "string") {
					throw new Error("types_array items must have string data type");
				}
			}
		}
		else {
			throw new Error("Types array is empty");
		}
		
		
		for (i = 0; i < types_array.length; i ++) {
			
			current_data_type = types_array[i].toLowerCase();
			
			/* check simple data types */
			if (current_data_type == "string" || current_data_type == "object" || current_data_type == "number") {		
				if (typeof func_link.arguments[i] != current_data_type) {
					return false;
				}
			}
			
			/* check complex data types */
			else {
				if (func_link.arguments[i].constructor.name != types_array[i]) {
					return false;
				}
			}
		}

		return true;
		
	}
	else {
		throw new Error("the second argument must be an array");
	}
	
}

I wanna know, what is more less and simply variant of the common function for check data types?
What are your suggestions? Thanks.

Hi @rihnovec93, first of all I wouldn’t use arguments.callee as it’s actually forbidden in strict mode, and not even used here – just pass the arguments object directly.

As for the type check itself, that might be simplified to something like this (using the array .every() method to check every provided type):

var checkTypes = function (args, types) {
  if (args.length !== types.length) {
    throw new Error('Invalid number of arguments')
  }
  
  return types.every(function (type, index) {
    return (
      // Primitive type check
      typeof args[index] === type 
    ) || (
      // Constructor name check
      typeof args[index] === 'function' && 
      args[index].constructor.name === type
    )
  })
}

// ...
checkTypes(arguments, ['string', 'Date'])

As an alternative to the constructor name check, you might use the constructor itself as a type argument; this way it also works with instance checks down the prototype chain:

var checkTypes = function (args, types) {
  if (args.length !== types.length) {
    throw new Error('Invalid number of arguments')
  }
  
  return types.every(function (type, index) {
    return (
      // Primitive type check
      typeof type === 'string' &&
      typeof args[index] === type
    ) || (
      // Prototype check
      typeof type === 'function' &&
      args[index] instanceof type
    )
  })
}

// ...
checkTypes(arguments, ['string', Date])

There’s one gotcha though as (e.g.) a primitive "string" is not an instance of String, so you have to be careful for which to check:

checkTypes(['foo'], [String])             // -> false
checkTypes(['foo'], ['string'])           // -> true
checkTypes([new String('foo')], [String]) // -> true

So if you don’t need that distinction I’d rather use constructors only, which can be achieved like so:

var checkTypes = function (args, types) {
  if (args.length !== types.length) {
    throw new Error('Invalid number of arguments')
  }
  
  return types.every(function (Type, index) {
    return (
      // Prototype check
      args[index] instanceof Type
    ) || (
      // Primitive type check
      typeof args[index] === Type.name.toLowerCase()
    )
  })
}

// ...
checkTypes(arguments, [String, Date])

This would work for both primitives and objects.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.