Skip to main content

Regular Expressions in TypeScript

TypeScript regex is a versatile tool for finding patterns in text, but it can be intimidating at first. It's important to learn how to use its features effectively. In this article, we'll cover the basics of TypeScript regex and explore more advanced techniques. Whether you're an experienced developer or just beginning, this guide will help you become skilled in using TypeScript regex to improve your coding abilities.

Pattern Matching with TypeScript Regex

In TypeScript, you can work with regular expressions using JavaScript's built-in RegExp object or regex literals. Here's a simple example:

// Using regex literal syntax (preferred for static patterns)
const pattern = /hello/;
const text = "hello world";

console.log(pattern.test(text)); // true

// Using RegExp constructor (useful when patterns are dynamic)
const dynamicPattern = new RegExp('hello');
console.log(dynamicPattern.test(text)); // true

When working with regex in TypeScript projects, you get the advantage of type checking and better code completion compared to plain JavaScript.

You can also use regex to check if a string contains specific patterns - a common task in form validation and data processing:

const text = "TypeScript makes regex easy to use";
const containsTypeScript = /typescript/i.test(text);
console.log(containsTypeScript); // true

Creating Effective Regular Expressions

The key to working with regex is understanding how to construct patterns that match exactly what you need. Let's look at some common pattern components:

// Match any digit
const digitRegex = /\d/;
console.log(digitRegex.test("5")); // true
console.log(digitRegex.test("a")); // false

// Match a specific number of characters
const postcodeRegex = /^\d{5}(-\d{4})?$/;
console.log(postcodeRegex.test("12345")); // true
console.log(postcodeRegex.test("12345-6789")); // true
console.log(postcodeRegex.test("1234")); // false

// Character classes
const lowercaseRegex = /[a-z]+/;
console.log(lowercaseRegex.test("hello")); // true
console.log(lowercaseRegex.test("HELLO")); // false

When working with complex patterns, it's often helpful to use the TypeScript playground to test and refine your expressions before integrating them into your code. For deeper understanding of how TypeScript processes these patterns, you might find the process shown in Convex's API generation code spelunking quite illuminating.

String Validation with Regular Expressions

Regex can be used to check if strings meet certain criteria. For example, to validate an email address:

const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const email = "example@example.com";

console.log(emailRegex.test(email)); // true

For more complex validation needs, you might use argument validation in Convex to ensure data consistency throughout your application.

Let's look at other common validation patterns:

// URL validation
const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w.-]*)*\/?$/;
console.log(urlRegex.test("https://www.example.com")); // true

// Password strength (at least 8 chars, one uppercase, one lowercase, one number)
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
console.log(passwordRegex.test("Password123")); // true

When working with validation, combining regex with utility types can make your code more robust and maintainable.

Extracting Data with Regular Expressions

To pull out parts of a string using regex, you can use the match() or exec() methods along with capture groups:

// Basic matching
const text = "Contact us at support@example.com for assistance";
const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;
const match = text.match(emailRegex);

console.log(match); // ["support@example.com"]

// Using capture groups to extract structured data
const dateString = "Born on 15-09-1986 in London";
const dateRegex = /(\d{2})-(\d{2})-(\d{4})/;
const dateMatch = dateString.match(dateRegex);

if (dateMatch) {
const [fullMatch, day, month, year] = dateMatch;
console.log(`Day: ${day}, Month: ${month}, Year: ${year}`);
// Day: 15, Month: 09, Year: 1986
}

Capture groups are particularly useful when working with complex string interpolation or when you need to extract multiple pieces of data from a single string.

String Replacement with TypeScript Regex

To change parts of a string using regex, you can use the replace() method:

// Basic replacement
const text = "Hello world";
const replaced = text.replace(/world/, "TypeScript");
console.log(replaced); // "Hello TypeScript"

// Using the global flag to replace all occurrences
const multipleReplacements = "apple, banana, apple".replace(/apple/g, "orange");
console.log(multipleReplacements); // "orange, banana, orange"

// Using capture groups in replacement
const formatted = "2023-03-15".replace(/(\d{4})-(\d{2})-(\d{2})/, "$2/$3/$1");
console.log(formatted); // "03/15/2023"

When working with more complex string transformations, you might need to split strings based on patterns before processing them.

Advanced Regex Features

Let's explore some more advanced regex capabilities in TypeScript:

// Lookahead assertions
const passwordWithLookahead = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
console.log(passwordWithLookahead.test("Pass1word!")); // true

// Named capture groups (TypeScript 4.0+)
const dateRegexNamed = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const dateMatchNamed = "2023-03-15".match(dateRegexNamed);

if (dateMatchNamed?.groups) {
console.log(`Day: ${dateMatchNamed.groups.day}`);
console.log(`Month: ${dateMatchNamed.groups.month}`);
console.log(`Year: ${dateMatchNamed.groups.year}`);
}

For complex data processing with regex, consider using TypeScript filters in Convex to handle advanced query scenarios.

Common Challenges and Solutions

Handling Escape Sequences

Remember that backslashes need to be escaped in string literals:

// Incorrect - single backslash in string
const badRegex = new RegExp('\d+');

// Correct - escaped backslash in string
const goodRegex = new RegExp('\\d+');

// Alternative - use regex literal
const betterRegex = /\d+/;

Managing Multiline Text

When working with multiline text, use the appropriate flags:

const multilineText = `Line 1
Line 2
Line 3`;

// Without multiline flag
console.log(/^Line/.test(multilineText)); // true (matches only at start of input)

// With multiline flag
console.log(/^Line/m.test(multilineText.slice(7))); // true (matches at start of any line)

Handling Regex Errors

When working with regex, it's important to handle potential errors properly with try catch blocks:

function validateEmail(email: string): boolean {
try {
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(email);
} catch (error) {
// Handle and log the error
console.error('Error validating email:', error);

// Determine the specific [`error type`](/optimization-error-handling/typescript-catch-error-type) for better error handling
if (error instanceof SyntaxError) {
console.error('Invalid regex syntax');
}

return false;
}
}

Optimizing Regex Performance

Poorly optimized regex can lead to performance issues:

// Inefficient - catastrophic backtracking possible
const inefficientRegex = /(\w+)*@example\.com/;

// More efficient alternative
const efficientRegex = /\w+@example\.com/;

When working with complex patterns, consider using TypeScript's type system to validate data before applying regex.

Final Thoughts about TypeScript Regex

Regular expressions in TypeScript offer a concise way to handle text processing tasks. By mastering the fundamentals and learning advanced techniques, you can write cleaner, more maintainable code for pattern matching, validation, and string manipulation. The examples in this guide provide a foundation for solving common regex challenges in TypeScript. As you apply these techniques in your projects, remember that readable regex is often more valuable than overly complex patterns. Start simple, test thoroughly, and refine your expressions as needed.