Function.prototype.addRuntimeChecker = function() { var handler = { apply: function(target, thisArg, argumentValues) { // https://stackoverflow.com/a/31194949 var argumentNames = target.toString() .replace(/[/][/].*$/mg,'') // strip single-line comments .replace(/\s+/g, '') // strip white space .replace(/[/][*][^/\*]*[*][/]/g, '') // strip multi-line comments .split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters .replace(/=[^,]+/g, '') // strip any ES6 defaults .split(',').filter(Boolean); // split & filter [""] var argumentNamesWithoutTypes = []; for (var i = 0; i < argumentValues.length; i++) { // Get type from parameter name var match = argumentNames[i].match(/(.+)_(\w+)/); if (!match || match.length < 3) { argumentNamesWithoutTypes.push(argumentNames[i]); continue; } argumentNamesWithoutTypes.push(match[1]); var type = match[2]; // Check types if (typeof argumentValues[i] !== type) throw new Error("Invalid argument #" + i + " - Expected " + type + ", got " + typeof argumentValues[i]); } // Assign actual names to window object before calling functions var oldWindow = {}; for (var i = 0; i < argumentNamesWithoutTypes.length; i++) { var name = argumentNamesWithoutTypes[i]; oldWindow[name] = window[name]; window[name] = argumentValues[i]; } // Call function var runtimeCheckerResult = target.apply(thisArg, argumentValues); // Re-assign old window values for (var name in oldWindow) { if (!oldWindow.hasOwnProperty(name)) continue; window[name] = oldWindow[name]; } return runtimeCheckerResult; } }; return new Proxy(this, handler); } window.typedFunction = function(func) { return func.addRuntimeChecker(); } window.typedVar = function(name, value) { function getTypeName(type) { return typeof type === "function" ? type.name : typeof type; } var scope = typedVar.caller || window; Object.defineProperty(scope, name, { set: function(newValue) { if ( (typeof value === "function" && !(newValue instanceof value)) || (typeof value !== "function" && typeof newValue !== typeof value) ) { throw new Error("Cannot assign value of type " + getTypeName(newValue) + " to variable " + name + " of type " + getTypeName(value)); } } }); }