Computers do numbers!
Compared to manipulating numbers, strings are very slow and use a lot of memory relative to what is being done.
To test if it is a perfect integer
const isPerfectInt = num => {
var sum = (num | 0) % 10;
while (num > 0) { sum += (num = num / 10 | 0) % 10 }
return sum === 10;
}
Improved cache
You are caching results in your answer and you only cache what you are looking for. However while you are searching you find all the perfect values below the one you are looking for. This negates most the advantage you get from caching calculations.
If you keep all calculated perfect numbers you can then use that last calculated value as a starting point. That way you will never test the same value twice.
const cache = [0,19]; // set up first value. Saves some messing about
const getNthPerfectNumber = n => {
if (cache[n]) { return cache[n] }
// Start from the last number tested plus one.
var idx = cache.length - 1, num = cache[idx] + 1;
while (idx < n) {
if (isPerfectInt(num)) { cache[++idx] = num }
num++;
}
return num - 1;
};