Check first if data.allItems[type] exists.
if(data.allItems[type] && data.allItems[type].length > 0){ ID = data.allItems[type][data.allItems[type].length - 1].id + 1; } else{ ID = 0; } It looks like the internals of your budgetController really depends on the fact that the arguments a) exist and b) are of a certain type (or value) in order to continue to function correctly.
In this case you might want to guard against allowing budgetController to proceed, with some error handling to stop execution if the arguments provided are unexpected.
var budgetController = (function() { //function constructor var Expense = function(id, description, value) { this.id = id; this.description = description; this.value = value; }; var Income = function(id, description, value) { this.id = id; this.description = description; this.value = value; }; var data = { allItems: { exp: [], inc: [] }, totals: { exp: 0, inc: 0 } }; return { addItem: function(type, des, val) { if (!Object.keys(data.allItems).includes(type)) { throw new Error(`Whoops! type must be one of ${Object.keys(data.allItems).join(', ')}`) } if (!des) { throw new Error(`Whoops! des is required`) } if (!val) { throw new Error(`Whoops! val is required`) } var newItem, ID; //create new id if (data.allItems[type].length > 0) { ID = data.allItems[type][data.allItems[type].length - 1].id + 1; } else { ID = 0; } //create new item based off of inc or exp type if (type === 'exp') { newItem = new Expense(ID, des, val); } else if (type === 'inc') { newItem = new Income(ID, des, val); } //push onto data structure data.allItems[type].push(newItem); //return the new element return newItem; }, testing: function() { console.log(data); } }; })(); var myBudgetController = budgetController myBudgetController.addItem('unacceptable type value', 'desc', 1)