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
| Name | Type | Description |
|---|---|---|
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