skip to Main Content

I am trying to validate a string to ensure it only contains certain characters using a function in my TypeScript project. The function takes a string and an options object as arguments, with the options specifying which characters are allowed.

Here’s the function:

/**
 * Validates a string to ensure it only contains allowed characters.
 * @param {string} name - The string to validate.
 * @param {Object} [options] - The options to use for validation.
 * @param {boolean} [options.allowNumbers=false] - Whether to allow numbers.
 * @param {boolean} [options.allowSpecialCharacters=false] - Whether to allow special characters.
 * @param {boolean} [options.allowSpaces=false] - Whether to allow spaces.
 * @param {boolean} [options.allowUnderscores=false] - Whether to allow underscores.
 * @param {boolean} [options.allowDashes=false] - Whether to allow dashes.
 * @param {boolean} [options.allowPunctuation=false] - Whether to allow punctuation.
 * @param {boolean} [options.allowCyrillic=false] - Whether to allow Cyrillic characters.
 * @param {boolean} [options.allowNonLatin=false] - Whether to allow non-Latin characters.
 * @returns {boolean} Whether the string is valid or not.
 */
interface IValidateNameOptions {
  allowNumbers?: boolean;
  allowSpecialCharacters?: boolean;
  allowSpaces?: boolean;
  allowUnderscores?: boolean;
  allowDashes?: boolean;
  allowPunctuation?: boolean;
  allowCyrillic?: boolean;
  allowNonLatin?: boolean;
}

interface IValidateName {
  (name: string, options?: IValidateNameOptions): boolean;
}

export const validateName: IValidateName = (
  name,
  options = {
    allowNumbers: false,
    allowSpecialCharacters: false,
    allowSpaces: false,
    allowUnderscores: false,
    allowDashes: false,
    allowPunctuation: false,
    allowCyrillic: false,
    allowNonLatin: false,
  },
) => {
  let regexStr = `^[a-zA-Z`;

  if (options.allowNonLatin) {
    regexStr += `\p{L}`;
  }
  if (options.allowNumbers) {
    regexStr += `0-9`;
  }
  if (options.allowSpecialCharacters) {
    regexStr += `\!\@\#\$\%\^\&\*\(\)\-\_\=\+\[\]\{\}\|\\\;\:\'\,\.\/\<\>\?`;
  }
  if (options.allowSpaces) {
    regexStr += `\s`;
  }
  if (options.allowUnderscores) {
    regexStr += `\_`;
  }
  if (options.allowDashes) {
    regexStr += `\-`;
  }
  if (options.allowPunctuation) {
    regexStr += `\p{P}`;
  }
  if (options.allowCyrillic) {
    regexStr += `\p{Cyrillic}`;
  }
  regexStr += `]+$`;

  const regex = new RegExp(regexStr, 'u');
  return regex.test(name);
};

Here is jest test:

it('should return true when name is valid and allowSpecialCharacters is true', () => {
  expect(validateName('Deri!', { allowSpecialCharacters: true })).toBe(true);
});

and here is error test:

 FAIL  src/tests/validateName.test.ts
  ✓ should return true when name is valid (3 ms)
  ✓ should return false when name is invalid (6 ms)
  ✕ should return true when name is valid and allowSpecialCharacters is true (2 ms)

  ● should return true when name is valid and allowSpecialCharacters is true

    SyntaxError: Invalid regular expression: /^[a-zA-Z!@#$%^&*()-_=+[]{}|\;:',./<>?]+$/: Invalid escape
        at new RegExp (<anonymous>)

      69 |   regexStr += `]+$`;
      70 |
    > 71 |   const regex = new RegExp(regexStr, 'u');
         |                 ^
      72 |   return regex.test(name);
      73 | };
      74 |

      at validateName (src/lib/validateName.ts:71:17)
      at Object.<anonymous> (src/tests/validateName.test.ts:41:22)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 2 passed, 3 total
Snapshots:   0 total
Time:        1.536 s
Ran all test suites.

However, I keep getting an "invalid escape" error when I try to use the function, even though I’ve escaped all the special characters. Can anyone tell me what I’m doing wrong? Thanks!

answer explanation and a correct RegExp code

3

Answers


  1. Some of your escaped characters are not specials in JavaScript. When you use Unicode mode with u flag, you cannot escape non-special characters "just in case". This is forbidden to ensure the possibility to add special escape sequences in future.

    Compare:

    console.log(/:/); // No errors.
    console.log(/:/); // No errors as there is no u flag.
    console.log(/:/u); // Throws error.

    So you only need to escape [ ^ $ . | ? * + ( ), or even less inside [].

    See https://javascript.info/regexp-escaping and https://javascript.info/regexp-character-sets-and-ranges#escaping-in- .

    Login or Signup to reply.
  2. If you run this in the browser you will get the same

    const reg1 = /^[a-zA-Z!@#$%^&*()-_=+[]{}|\;:',./<>?]+$/u
    console.log(reg1)
    

    the problem is you have specified – /u
    Which means its expecting backslash characters to preceed with the u like this

    this works:

    // u003a === :
    const reg = /u003a/u
    const matched = ' :'.match(reg)
    console.log(matched)
    
    Login or Signup to reply.
  3. The problem seems to be with the 'u' option. Don’t know exactly what changes with that (other than allowing unicode escapes) but it seems like it treats escape characters a bit differently.

    The thing is inside [] you don’t need to escape special characters, with a few exceptions. Also, to write raw strings i Javascript you can use String.raw`` .

    So this should work:

    if (options.allowSpecialCharacters) {
        regexStr += String.raw`!@#$%^&*()-_=+[]{}|\;:',./<>?`;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search