You get true from test because there is a character in the string that matches the expression. The expression has no anchors, so it's not that it requires all characters in the string to match, just one. To require all characters to match, you'd need a "start of input" assertion (^) at the beginning, an "end of input" assertion ($) at the end, and either a "zero or more" (*) or "one or more" (+) quantifier on the character class (depending on whether an empty string should pass).
If you're getting the expression from elsewhere, you can add those to it after the fact:
const whiteList = /[@A-Za-z0-9.,-]/g // Regex from external source. It will be difficult to modify.
const str= 'ds%d';
console.log(str.replace(whiteList, '').length === 0);
const improvedList = new RegExp("^" + whiteList.source + "+$");
console.log(improvedList.test(str)); // Now shows false
That does make the assumption that the original regex has the problem described. You might check first, but it would be easy to construct regular expressions that seemed like they needed modifying but didn't.
Alternatively, just use the replace check you have, since it works as well. It's not that much more expensive.