본문으로 건너뛰기

isValidRegexPattern

Determines whether a regex pattern string is valid with enhanced safety. Validates regex pattern strings by attempting to construct a RegExp object, providing safe pattern validation without throwing exceptions. Useful for user input validation and dynamic regex compilation scenarios.

Signature

const isValidRegexPattern: (pattern: string) => pattern is string

Parameters

NameTypeDescription
pattern-String to test as a valid regex pattern

Returns

Type-safe boolean indicating whether the pattern is valid

Examples

Basic regex pattern validation

import { isValidRegexPattern } from '@winglet/common-utils';

// True cases - valid patterns
console.log(isValidRegexPattern('hello')); // true (simple string)
console.log(isValidRegexPattern('\\d+')); // true (digits)
console.log(isValidRegexPattern('[a-z]+')); // true (character class)
console.log(isValidRegexPattern('(foo|bar)')); // true (alternation)
console.log(isValidRegexPattern('^start.*end$')); // true (anchors)
console.log(isValidRegexPattern('\\w{2,5}')); // true (quantifiers)
console.log(isValidRegexPattern('')); // true (empty pattern)

// False cases - invalid patterns
console.log(isValidRegexPattern('[')); // false (unclosed bracket)
console.log(isValidRegexPattern('(')); // false (unclosed parenthesis)
console.log(isValidRegexPattern('*')); // false (invalid quantifier)
console.log(isValidRegexPattern('(?')); // false (incomplete group)
console.log(isValidRegexPattern('\\x')); // false (incomplete escape)
console.log(isValidRegexPattern('{5,2}')); // false (invalid range)

User input validation

interface SearchForm {
query: string;
useRegex: boolean;
caseSensitive: boolean;
}

function validateSearchForm(form: SearchForm): string[] {
const errors: string[] = [];

if (!form.query.trim()) {
errors.push('Search query cannot be empty');
}

if (form.useRegex && !isValidRegexPattern(form.query)) {
errors.push('Invalid regular expression pattern');
}

return errors;
}

function performSearch(form: SearchForm, text: string): string[] {
const validation = validateSearchForm(form);
if (validation.length > 0) {
throw new Error(`Form validation failed: ${validation.join(', ')}`);
}

let regex: RegExp;

if (form.useRegex) {
// We know it's valid because we validated it
const flags = form.caseSensitive ? 'g' : 'gi';
regex = new RegExp(form.query, flags);
} else {
// Escape special regex characters for literal search
const escaped = form.query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');
const flags = form.caseSensitive ? 'g' : 'gi';
regex = new RegExp(escaped, flags);
}

return text.match(regex) || [];
}

Dynamic regex builder with validation

class RegexBuilder {
private patterns: string[] = [];

add(pattern: string): this {
if (!isValidRegexPattern(pattern)) {
throw new Error(`Invalid regex pattern: ${pattern}`);
}
this.patterns.push(pattern);
return this;
}

combine(operator: '|' | '&' = '|'): RegExp {
if (this.patterns.length === 0) {
throw new Error('No patterns added to builder');
}

let combined: string;

if (operator === '|') {
// OR operation - alternation
combined = `(${this.patterns.join('|')})`;
} else {
// AND operation - lookaheads
const lookaheads = this.patterns.map(p => `(?=.*${p})`).join('');
combined = `${lookaheads}.*`;
}

if (!isValidRegexPattern(combined)) {
throw new Error('Generated pattern is invalid');
}

return new RegExp(combined);
}

validate(): { valid: boolean; errors: string[] } {
const errors: string[] = [];

this.patterns.forEach((pattern, index) => {
if (!isValidRegexPattern(pattern)) {
errors.push(`Pattern ${index + 1} is invalid: ${pattern}`);
}
});

return {
valid: errors.length === 0,
errors
};
}
}

// Usage
const builder = new RegexBuilder();
builder.add('\\\\d+').add('[a-z]+').add('test');
const regex = builder.combine('|'); // Matches digits OR letters OR 'test'

Configuration validation

interface RegexConfig {
patterns: string[];
flags?: string;
enabled: boolean;
}

function validateRegexConfig(config: RegexConfig): {
valid: boolean;
errors: string[];
compiledPatterns?: RegExp[];
} {
const errors: string[] = [];
const compiledPatterns: RegExp[] = [];

if (!config.enabled) {
return { valid: true, errors: [], compiledPatterns: [] };
}

if (!Array.isArray(config.patterns) || config.patterns.length === 0) {
errors.push('Patterns array is required and cannot be empty');
return { valid: false, errors };
}

config.patterns.forEach((pattern, index) => {
if (typeof pattern !== 'string') {
errors.push(`Pattern ${index + 1} must be a string`);
return;
}

if (!isValidRegexPattern(pattern)) {
errors.push(`Pattern ${index + 1} is invalid: ${pattern}`);
return;
}

try {
const regex = new RegExp(pattern, config.flags);
compiledPatterns.push(regex);
} catch (error) {
errors.push(`Pattern ${index + 1} failed to compile with flags: ${error.message}`);
}
});

return {
valid: errors.length === 0,
errors,
compiledPatterns: errors.length === 0 ? compiledPatterns : undefined
};
}

Text processor with pattern validation

interface TextRule {
name: string;
pattern: string;
replacement: string;
flags?: string;
}

class TextProcessor {
private rules: Array<{ name: string; regex: RegExp; replacement: string }> = [];

addRule(rule: TextRule): void {
if (!isValidRegexPattern(rule.pattern)) {
throw new Error(`Invalid pattern in rule '${rule.name}': ${rule.pattern}`);
}

try {
const regex = new RegExp(rule.pattern, rule.flags || 'g');
this.rules.push({
name: rule.name,
regex,
replacement: rule.replacement
});
} catch (error) {
throw new Error(`Failed to compile rule '${rule.name}': ${error.message}`);
}
}

validateRules(rules: TextRule[]): { valid: TextRule[]; invalid: Array<{ rule: TextRule; error: string }> } {
const valid: TextRule[] = [];
const invalid: Array<{ rule: TextRule; error: string }> = [];

rules.forEach(rule => {
if (!isValidRegexPattern(rule.pattern)) {
invalid.push({ rule, error: 'Invalid regex pattern' });
return;
}

try {
new RegExp(rule.pattern, rule.flags);
valid.push(rule);
} catch (error) {
invalid.push({ rule, error: error.message });
}
});

return { valid, invalid };
}

process(text: string): string {
let result = text;

this.rules.forEach(rule => {
result = result.replace(rule.regex, rule.replacement);
});

return result;
}
}

Pattern testing utility

function testRegexPattern(pattern: string, testStrings: string[]) {
if (!isValidRegexPattern(pattern)) {
return {
valid: false,
error: 'Invalid regex pattern',
results: []
};
}

try {
const regex = new RegExp(pattern, 'g');
const results = testStrings.map(str => ({
input: str,
matches: str.match(regex) || [],
hasMatch: regex.test(str)
}));

return {
valid: true,
error: null,
results
};
} catch (error) {
return {
valid: false,
error: error.message,
results: []
};
}
}

// Usage
const testResult = testRegexPattern('\\\\d+', ['abc', '123', 'test456']);
console.log(testResult);
// {
// valid: true,
// error: null,
// results: [
// { input: 'abc', matches: [], hasMatch: false },
// { input: '123', matches: ['123'], hasMatch: true },
// { input: 'test456', matches: ['456'], hasMatch: true }
// ]
// }

Playground

import { isValidRegexPattern } from '@winglet/common-utils';

// True cases - valid patterns
console.log(isValidRegexPattern('hello')); // true (simple string)
console.log(isValidRegexPattern('\d+')); // true (digits)
console.log(isValidRegexPattern('[a-z]+')); // true (character class)
console.log(isValidRegexPattern('(foo|bar)')); // true (alternation)
console.log(isValidRegexPattern('^start.*end$')); // true (anchors)
console.log(isValidRegexPattern('\w{2,5}')); // true (quantifiers)
console.log(isValidRegexPattern('')); // true (empty pattern)

// False cases - invalid patterns
console.log(isValidRegexPattern('[')); // false (unclosed bracket)
console.log(isValidRegexPattern('(')); // false (unclosed parenthesis)
console.log(isValidRegexPattern('*')); // false (invalid quantifier)
console.log(isValidRegexPattern('(?')); // false (incomplete group)
console.log(isValidRegexPattern('\x')); // false (incomplete escape)
console.log(isValidRegexPattern('{5,2}')); // false (invalid range)

Notes

Validation Method:

  • Uses try-catch with RegExp constructor
  • Safe - never throws exceptions
  • Returns false for any pattern that would cause RegExp to throw
  • Does not validate semantic correctness, only syntactic validity

Common Invalid Patterns:

  • Unclosed character classes: [abc
  • Unclosed groups: (pattern
  • Invalid quantifiers: *, {5,2}
  • Incomplete escapes: \\x, \\u
  • Invalid unicode escapes: \\u{}

Use Cases:

  • User input validation (search forms, configuration)
  • Dynamic regex compilation
  • Pattern builder utilities
  • Configuration file validation
  • Text processing rule validation

Performance:

  • Lightweight validation using native RegExp constructor
  • Early return on construction success
  • Exception handling overhead only for invalid patterns

Related Functions:

  • Use isRegex() to check for existing RegExp objects
  • Use RegExp() constructor for actual pattern compilation
  • Use isString() for basic string validation