Referencia de expresiones regulares
Una referencia completa para expresiones regulares de JavaScript/TypeScript — sintaxis, banderas, métodos y patrones del mundo real comúnmente usados en aplicaciones React.
Referencia de eventos
| Método | Devuelve | Descripción |
|---|---|---|
regex.test(str) | boolean | Devuelve true si el patrón coincide en cualquier lugar de la cadena |
regex.exec(str) | RegExpExecArray | null | Devuelve detalles de coincidencia (grupos, índice) o null |
str.match(regex) | RegExpMatchArray | null | Devuelve coincidencias — el comportamiento cambia con la bandera g |
str.matchAll(regex) | IterableIterator<RegExpMatchArray> | Devuelve iterador de todas las coincidencias (requiere bandera g) |
str.search(regex) | number | Devuelve el índice de la primera coincidencia, o -1 |
str.replace(regex, replacement) | string | Reemplaza la primera coincidencia (o todas con bandera g) |
str.replaceAll(regex, replacement) | string | Reemplaza todas las coincidencias (requiere bandera g) |
str.split(regex) | string[] | Divide la cadena por coincidencias de expresión regular |
Receta
Referencia rápida — operaciones de expresión regular más comunes en TypeScript.
// Comprueba si una cadena coincide
const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test("user@example.com"); // true
// Extrae coincidencias
const matches = "2024-01-15".match(/(\d{4})-(\d{2})-(\d{2})/);
// matches[1] = "2024", matches[2] = "01", matches[3] = "15"
// Grupos de captura nombrados
const result = "2024-01-15".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
// result?.groups?.year = "2024"
// Reemplaza con patrón
const slug = "Hello World! Foo".replace(/[^a-zA-Z0-9]+/g, "-").toLowerCase();
// "hello-world-foo"
// Divide por patrón
const words = "one, two; three".split(/[,;]\s*/);
// ["one", "two", "three"]Cuándo usar esto: Validación de entrada, análisis de texto, enrutamiento de URL, lógica de búsqueda/filtrado y transformación de datos en formularios y utilidades.
Ejemplo de trabajo
"use client";
import { useState } from "react";
interface ValidationResult {
field: string;
value: string;
isValid: boolean;
message: string;
}
const VALIDATORS: Record<string, { pattern: RegExp; message: string }> = {
email: {
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: "Debe ser una dirección de correo electrónico válida",
},
phone: {
pattern: /^\+?[\d\s\-()]{7,15}$/,
message: "Debe ser un número de teléfono válido (7-15 dígitos)",
},
url: {
pattern: /^https?:\/\/[^\s/$.?#].[^\s]*$/i,
message: "Debe ser una URL válida comenzando con http(s)://",
},
zipCode: {
pattern: /^\d{5}(-\d{4})?$/,
message: "Debe ser un código postal estadounidense válido (12345 o 12345-6789)",
},
password: {
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/,
message: "Mín 8 caracteres, 1 mayúscula, 1 minúscula, 1 dígito, 1 carácter especial",
},
};
export default function RegexValidationDemo() {
const [results, setResults] = useState<ValidationResult[]>([]);
const [input, setInput] = useState("");
const [selected, setSelected] = useState("email");
function validate() {
const { pattern, message } = VALIDATORS[selected];
const isValid = pattern.test(input);
setResults((prev) => [
{ field: selected, value: input, isValid, message: isValid ? "Válido" : message },
...prev,
]);
}
return (
<div className="space-y-4 p-4 max-w-md">
<select
value={selected}
onChange={(e) => setSelected(e.target.value)}
className="border rounded px-2 py-1 w-full"
>
{Object.keys(VALIDATORS).map((key) => (
<option key={key} value={key}>{key}</option>
))}
</select>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder={`Ingresa ${selected}...`}
className="border rounded px-2 py-1 w-full"
/>
<button onClick={validate} className="bg-blue-600 text-white px-4 py-1 rounded">
Validar
</button>
<ul className="space-y-1 text-sm">
{results.map((r, i) => (
<li key={i} className={r.isValid ? "text-green-600" : "text-red-600"}>
<strong>{r.field}:</strong> "{r.value}" — {r.message}
</li>
))}
</ul>
</div>
);
}Lo que esto demuestra:
- Usar
RegExp.test()para validación booleana - Almacenar patrones de expresión regular en un objeto de búsqueda tipado
- Patrones de validación comunes del mundo real (correo electrónico, teléfono, URL, código postal, contraseña)
Análisis profundo
Referencia de sintaxis de expresión regular
Clases de caracteres
// Predefinidos
/\d/ // Dígito [0-9]
/\D/ // No dígito [^0-9]
/\w/ // Carácter de palabra [a-zA-Z0-9_]
/\W/ // Carácter que no es de palabra
/\s/ // Espacio en blanco (espacio, tabulación, salto de línea)
/\S/ // No espacio en blanco
/./ // Cualquier carácter excepto salto de línea
// Personalizados
/[abc]/ // Cualquiera de a, b o c
/[a-z]/ // Rango: a a z
/[^abc]/ // NO a, b o c (negación)
/[a-zA-Z]/ // Cualquier letra
/[0-9]/ // Igual que \dCuantificadores
/a*/ // 0 o más
/a+/ // 1 o más
/a?/ // 0 o 1
/a{3}/ // Exactamente 3
/a{2,5}/ // 2 a 5
/a{2,}/ // 2 o más
// Codicioso vs perezoso
/".+"/ // Codicioso: coincide "foo" y "bar" como una coincidencia en: "foo" y "bar"
/".+?"/ // Perezoso: coincide "foo" luego "bar" por separadoAnclajes y límites
/^hello/ // Comienza con "hello"
/world$/ // Termina con "world"
/\bhello\b/ // Palabra completa "hello" (límite de palabra)
/\Bhello/ // NO en un límite de palabraGrupos y alternancia
// Grupo de captura
/(foo|bar)/ // Coincide "foo" o "bar", lo captura
/(\d{3})-(\d{4})/ // Captura código de área y número por separado
// Grupo sin captura
/(?:foo|bar)/ // Agrupa sin capturar
// Grupo de captura nombrado
/(?<area>\d{3})-(?<number>\d{4})/
// Referencia hacia atrás
/(\w+)\s+\1/ // Coincide palabras repetidas: "the the"Anticipación y retrospectiva
// Anticipación positiva: coincide "foo" seguido de "bar"
/foo(?=bar)/ // "foobar" ✓ "foobaz" ✗
// Anticipación negativa: coincide "foo" NO seguido de "bar"
/foo(?!bar)/ // "foobaz" ✓ "foobar" ✗
// Retrospectiva positiva: coincide "bar" precedido de "foo"
/(?<=foo)bar/ // "foobar" ✓ "bazbar" ✗
// Retrospectiva negativa: coincide "bar" NO precedido de "foo"
/(?<!foo)bar/ // "bazbar" ✓ "foobar" ✗Banderas
/pattern/g // Global — encuentra todas las coincidencias, no solo la primera
/pattern/i // Insensible a mayúsculas y minúsculas
/pattern/m // Multilínea — ^ y $ coinciden con inicios/finales de línea
/pattern/s // Dotall — . coincide también con saltos de línea
/pattern/u // Unicode — soporte Unicode adecuado
/pattern/d // Índices — incluye índices de inicio/fin en resultados
/pattern/v // Conjuntos Unicode (ES2024) — operaciones de conjunto en clases de caracteres
// Combina banderas
/pattern/gi // Global + insensible a mayúsculasMétodos de JavaScript en detalle
test() — Comprobación booleana
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
emailRegex.test("user@site.com"); // true
emailRegex.test("not-an-email"); // false
// ADVERTENCIA: test() con bandera /g tiene estado
const re = /a/g;
re.test("abc"); // true (lastIndex = 1)
re.test("abc"); // false (lastIndex = 0, reinicia)
re.test("abc"); // true (el ciclo se repite)exec() — Información detallada de coincidencia
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const result = dateRegex.exec("Date: 2024-03-15");
if (result) {
result[0]; // "2024-03-15" (coincidencia completa)
result[1]; // "2024" (grupo 1)
result.index; // 6 (posición en cadena)
result.groups?.year; // "2024"
result.groups?.month; // "03"
result.groups?.day; // "15"
}match() — Método de cadena
// Sin /g: igual que exec()
"Price: $42.50".match(/\$(\d+\.\d{2})/);
// ["$42.50", "42.50", index: 7, groups: undefined]
// Con /g: devuelve array de todas las coincidencias (sin grupos)
"cat bat hat".match(/[a-z]at/g);
// ["cat", "bat", "hat"]matchAll() — Itera todas las coincidencias con grupos
const text = "Jan 15, Feb 20, Mar 5";
const regex = /(?<month>[A-Z][a-z]+)\s+(?<day>\d+)/g;
for (const match of text.matchAll(regex)) {
console.log(`${match.groups?.month} ${match.groups?.day}`);
}
// "Jan 15"
// "Feb 20"
// "Mar 5"
// O convierte a array
const allMatches = [...text.matchAll(regex)];replace() y replaceAll()
// Reemplazo simple
"hello world".replace(/world/, "TypeScript"); // "hello TypeScript"
// Reemplazo global
"aabbcc".replace(/b/g, "X"); // "aaXXcc"
// Con grupos de captura ($1, $2, etc.)
"John Smith".replace(/(\w+)\s(\w+)/, "$2, $1"); // "Smith, John"
// Con grupos nombrados
"2024-01-15".replace(
/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/,
"$<m>/$<d>/$<y>"
); // "01/15/2024"
// Con una función
"hello world".replace(/\b\w/g, (char) => char.toUpperCase());
// "Hello World"
// Reemplazo de función compleja
"img_001.png, img_002.jpg".replace(
/img_(\d+)\.(png|jpg)/g,
(_match, num, ext) => `photo-${parseInt(num)}.${ext}`
);
// "photo-1.png, photo-2.jpg"split() — Divide por patrón
// Divide por múltiples delimitadores
"one, two; three four".split(/[,;\s]+/);
// ["one", "two", "three", "four"]
// Divide manteniendo el delimitador (grupo de captura)
"one+two-three".split(/([+-])/);
// ["one", "+", "two", "-", "three"]
// Limita resultados
"a-b-c-d-e".split(/-/, 3);
// ["a", "b", "c"]Variaciones
Variación 1: Validación de correo electrónico
// Validación básica de correo electrónico
const basicEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Correo electrónico más estricto (patrón común del mundo real)
const stricterEmail = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
basicEmail.test("user@example.com"); // true
basicEmail.test("user@sub.domain.co.uk"); // true
basicEmail.test("user@.com"); // falseVariación 2: Análisis de URL
const urlRegex = /^(?<protocol>https?):\/\/(?<host>[^/:]+)(?::(?<port>\d+))?(?<path>\/[^?#]*)?(?:\?(?<query>[^#]*))?(?:#(?<fragment>.*))?$/;
const parsed = urlRegex.exec("https://example.com:8080/api/users?page=2#top");
if (parsed?.groups) {
parsed.groups.protocol; // "https"
parsed.groups.host; // "example.com"
parsed.groups.port; // "8080"
parsed.groups.path; // "/api/users"
parsed.groups.query; // "page=2"
parsed.groups.fragment; // "top"
}Variación 3: Verificador de fortaleza de contraseña
function checkPasswordStrength(password: string): string[] {
const checks = [
{ regex: /.{8,}/, label: "Al menos 8 caracteres" },
{ regex: /[a-z]/, label: "Letra minúscula" },
{ regex: /[A-Z]/, label: "Letra mayúscula" },
{ regex: /\d/, label: "Un dígito" },
{ regex: /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/, label: "Carácter especial" },
{ regex: /^(?!.*(.)\1{2})/, label: "Sin 3+ caracteres repetidos" },
];
return checks.filter((c) => !c.regex.test(password)).map((c) => c.label);
}
checkPasswordStrength("weak"); // ["Al menos 8 caracteres", "Letra mayúscula", "Un dígito", "Carácter especial"]
checkPasswordStrength("Str0ng!Pass"); // []Variación 4: Generador de slug
function toSlug(text: string): string {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, "") // Elimina caracteres que no sean palabra (excepto espacios e guiones)
.replace(/[\s_]+/g, "-") // Reemplaza espacios/guiones bajos con guiones
.replace(/-+/g, "-") // Colapsa múltiples guiones
.replace(/^-|-$/g, ""); // Recorta guiones iniciales/finales
}
toSlug("Hello, World! This is a Test"); // "hello-world-this-is-a-test"
toSlug(" --Foo & Bar-- "); // "foo--bar"Variación 5: Extrae números del texto
function extractNumbers(text: string): number[] {
return [...text.matchAll(/-?\d+\.?\d*/g)].map((m) => parseFloat(m[0]));
}
extractNumbers("Price: $42.50, Qty: 3, Discount: -5.25");
// [42.5, 3, -5.25]Variación 6: Interpolación de cadena de plantilla
function interpolate(template: string, vars: Record<string, string>): string {
return template.replace(/\{\{(\w+)\}\}/g, (_match, key: string) => vars[key] ?? `{{${key}}}`);
}
interpolate("Hello {{name}}, welcome to {{place}}!", {
name: "Alice",
place: "Wonderland",
});
// "Hello Alice, welcome to Wonderland!"Variación 7: Resalta coincidencias de búsqueda (React)
function highlightMatches(text: string, query: string): (string | JSX.Element)[] {
if (!query.trim()) return [text];
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const regex = new RegExp(`(${escaped})`, "gi");
const parts = text.split(regex);
return parts.map((part, i) =>
regex.test(part) ? <mark key={i} className="bg-yellow-200">{part}</mark> : part
);
}
// Uso: <p>{highlightMatches("Hello World", "world")}</p>
// Renderiza: Hello <mark>World</mark>Variación 8: Sanitiza entidades HTML
function stripHtmlTags(html: string): string {
return html.replace(/<[^>]*>/g, "");
}
stripHtmlTags("<p>Hello <strong>world</strong></p>"); // "Hello world"
function escapeRegex(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
escapeRegex("Price: $10.00 (USD)"); // "Price: \\$10\\.00 \\(USD\\)"Variación 9: Analiza línea CSV
function parseCSVLine(line: string): string[] {
const result: string[] = [];
const regex = /(?:^|,)("(?:[^"]|"")*"|[^,]*)/g;
let match: RegExpExecArray | null;
while ((match = regex.exec(line)) !== null) {
let value = match[1];
if (value.startsWith('"') && value.endsWith('"')) {
value = value.slice(1, -1).replace(/""/g, '"');
}
result.push(value);
}
return result;
}
parseCSVLine('Alice,30,"New York, NY","She said ""hi"""');
// ["Alice", "30", "New York, NY", 'She said "hi"']Variación 10: Formateador de número de teléfono
function formatPhone(raw: string): string {
const digits = raw.replace(/\D/g, "");
const match = digits.match(/^(\d{1,3})?(\d{3})(\d{3})(\d{4})$/);
if (!match) return raw;
const [, country, area, prefix, line] = match;
return country ? `+${country} (${area}) ${prefix}-${line}` : `(${area}) ${prefix}-${line}`;
}
formatPhone("12125551234"); // "+1 (212) 555-1234"
formatPhone("2125551234"); // "(212) 555-1234"Variación 11: Detección de tarjeta de crédito
const CARD_PATTERNS: Record<string, RegExp> = {
visa: /^4\d{12}(?:\d{3})?$/,
mastercard: /^5[1-5]\d{14}$/,
amex: /^3[47]\d{13}$/,
discover: /^6(?:011|5\d{2})\d{12}$/,
};
function detectCardType(number: string): string | null {
const cleaned = number.replace(/\D/g, "");
for (const [type, pattern] of Object.entries(CARD_PATTERNS)) {
if (pattern.test(cleaned)) return type;
}
return null;
}
detectCardType("4111 1111 1111 1111"); // "visa"
detectCardType("5500 0000 0000 0004"); // "mastercard"Variación 12: Extrae hashtags y menciones
function extractHashtags(text: string): string[] {
return [...text.matchAll(/#(\w+)/g)].map((m) => m[1]);
}
function extractMentions(text: string): string[] {
return [...text.matchAll(/@(\w+)/g)].map((m) => m[1]);
}
const tweet = "Hey @alice and @bob, check out #TypeScript #React!";
extractHashtags(tweet); // ["TypeScript", "React"]
extractMentions(tweet); // ["alice", "bob"]Variación 13: Validación de dirección IP
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
ipv4Regex.test("192.168.1.1"); // true
ipv4Regex.test("256.1.1.1"); // false
ipv4Regex.test("10.0.0.255"); // trueVariación 14: Extractor de enlace Markdown
function extractMarkdownLinks(md: string): Array<{ text: string; url: string }> {
return [...md.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)].map((m) => ({
text: m[1],
url: m[2],
}));
}
extractMarkdownLinks("Visit [Google](https://google.com) or [GitHub](https://github.com)");
// [{ text: "Google", url: "https://google.com" }, { text: "GitHub", url: "https://github.com" }]Variación 15: Conversión entre camelCase y snake_case
function camelToSnake(str: string): string {
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
}
function snakeToCamel(str: string): string {
return str.replace(/_([a-z])/g, (_match, letter: string) => letter.toUpperCase());
}
camelToSnake("backgroundColor"); // "background_color"
snakeToCamel("background_color"); // "backgroundColor"
function toKebabCase(str: string): string {
return str
.replace(/([a-z])([A-Z])/g, "$1-$2")
.replace(/[\s_]+/g, "-")
.toLowerCase();
}
toKebabCase("backgroundColor"); // "background-color"
toKebabCase("Hello World"); // "hello-world"Notas de TypeScript
// Tipo RegExp
const pattern: RegExp = /\d+/g;
// RegExpMatchArray — devuelto por String.match() / RegExp.exec()
const result: RegExpMatchArray | null = "abc123".match(/(\d+)/);
if (result) {
const full: string = result[0];
const group: string = result[1];
const idx: number = result.index ?? 0;
}
// RegExpExecArray con grupos nombrados
const dateMatch = /(?<year>\d{4})-(?<month>\d{2})/.exec("2024-03");
if (dateMatch?.groups) {
// groups es Record<string, string> — requiere encadenamiento opcional
const year: string | undefined = dateMatch.groups.year;
}
// Mapa de validador con seguridad de tipo
interface Validator {
pattern: RegExp;
message: string;
}
const validators: Record<string, Validator> = {
email: { pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: "Correo electrónico inválido" },
};
// Tipo de literal de plantilla con validación de expresión regular (solo en tiempo de compilación)
type HexColor = `#${string}`;
function setColor(color: HexColor) { /* ... */ }
setColor("#ff0000"); // OK
// setColor("red"); // Error de tipoInconvenientes
-
test()con bandera/gtiene estado — El objeto de expresión regular rastrealastIndex. Llamar atest()repetidamente en diferentes cadenas da resultados inesperados. Solución: Omite la banderagcuando usestest(), o crea una nueva expresión regular cada vez. -
Olvidar escapar caracteres especiales — Caracteres como
.,*,+,?,(,),[,{,$,^,|,\tienen significado especial. Solución: Usa un ayudante:str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")para escapar entrada del usuario antes de construir una expresión regular. -
Coincidencia codiciosa captura demasiado —
/".+"/.exec('"foo" and "bar"')coincide"foo" and "bar"como una coincidencia. Solución: Usa cuantificador perezoso".+?"para coincidir la cadena más corta posible. -
match()con/gelimina grupos de captura — Con la bandera global,match()devuelve solo coincidencias completas, no grupos. Solución: UsamatchAll()en su lugar cuando necesites información de grupo con coincidencia global. -
Retroceso catastrófico — Patrones como
/(a+)+$/en cadenas largas que no coinciden pueden congelar el navegador. Solución: Evita cuantificadores anidados en los mismos caracteres. Usa patrones atómicos o prueba con verificadores de ReDoS. -
\bno funciona con Unicode — Los límites de palabra usan la definición ASCII de "carácter de palabra" ([a-zA-Z0-9_]). Caracteres acentuados comoése tratan como no-palabra. Solución: Usa la bandera/uy límites explícitos cuando trabajes con texto internacional. -
Los saltos de línea no coinciden con
.— Por defecto,.coincide con cualquier carácter excepto\n. Solución: Usa la bandera/s(dotall) para hacer que.coincida con saltos de línea, o usa[\s\S]como alternativa universal. -
Olvidar anclajes
^y$en validación —/\d+/.test("abc123xyz")devuelvetrueporque encuentra dígitos en cualquier lugar. Solución: Usa^y$para reforzar coincidencia de cadena completa:/^\d+$/. -
replace()solo reemplaza la primera coincidencia — Sin la banderag, solo se reemplaza la primera ocurrencia. Solución: Siempre usa bandera/goreplaceAll()cuando quieras reemplazar todas las ocurrencias. -
Construir expresión regular de entrada del usuario sin escapar — Si un usuario escribe
(hello)en una caja de búsqueda y usasnew RegExp(input), crea un grupo de captura en lugar de coincidir paréntesis literales. Solución: Siempre escapa entrada del usuario antes de pasar anew RegExp().
Alternativas
| Alternativa | Usa cuando | No uses cuando |
|---|---|---|
String.includes() | Comprobación de subcadena simple sin coincidencia de patrón | Necesitas comodines o lógica de patrón |
String.startsWith() / endsWith() | Comprobar prefijo o sufijo | El prefijo/sufijo varía por patrón |
Zod .regex() | Validación de esquema en formularios con zod | Necesitas extraer o transformar contenido coincidente |
| URL / URLSearchParams API | Analizar URLs | Coincidir patrones similares a URL arbitrarios en texto |
Intl.NumberFormat | Formatear números para visualización | Extraer números de texto mixto |
Librerías de analizador (p. ej., date-fns) | Análisis complejo de fecha/hora | Coincidencia de formato de fecha simple |
Preguntas frecuentes
¿Cuál es la diferencia entre test() y match()?
test()es un método de RegExp que devuelve un booleano — úsalo cuando solo necesites saber si un patrón coincidematch()es un método de String que devuelve los detalles de coincidencia (coincidencia completa, grupos, índice) o nulltest()es más rápido cuando no necesitas detalles de coincidencia- Ambos tienen estado con la bandera
/g—test()actualizalastIndexen el objeto de expresión regular
¿Cuándo debo usar exec() vs matchAll()?
exec()devuelve una coincidencia a la vez — llámalo en un bucle con una expresión regular/gpara iterarmatchAll()devuelve un iterador de todas las coincidencias a la vez — más limpio y menos propenso a erroresmatchAll()requiere la bandera/g;exec()funciona con o sin ella- Prefiere
matchAll()en código moderno — evita la trampa con estadolastIndexde buclesexec()
¿Cómo usar de forma segura entrada del usuario en una expresión regular?
- Siempre escapa caracteres especiales de expresión regular antes de usar
new RegExp(userInput) - Usa esta función de escape:
input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") - Nunca pases entrada bruta del usuario a
new RegExp()— puede causar errores de sintaxis o ataques ReDoS - Considera usar
String.includes()para coincidencia de subcadena simple en su lugar
¿Qué son grupos de captura nombrados y por qué usarlos?
- Sintaxis:
(?<name>pattern)— asigna un nombre a un grupo de captura - Accede a través de
match.groups.nameen lugar dematch[1]posicional - Hace el código más legible y menos frágil cuando la expresión regular cambia
- Disponible en ES2018+ — soportado en todos los navegadores modernos y Node.js 10+
¿Qué es retroceso catastrófico y cómo lo evito?
- Ocurre cuando una expresión regular tiene cuantificadores anidados como
(a+)+que crean rutas de coincidencia exponenciales - En entrada que no coincide, el motor intenta cada combinación antes de fallar — puede congelar el navegador
- Evita patrones como
(x+)+,(x+)*,(x*)*en el mismo conjunto de caracteres - Prueba patrones con una herramienta de verificador de ReDoS antes de usar en producción
- Usa grupos atómicos o cuantificadores posesivos donde estén disponibles
¿Cómo difieren anticipación y retrospectiva de grupos normales?
- Anticipación
(?=...)y retrospectiva(?<=...)son de ancho cero — afirman una condición sin consumir caracteres - Los grupos normales
(...)consumen el texto coincidente e lo incluyen en el resultado - Usa anticipación positiva
(?=...)para coincidir "seguido de" sin incluir el texto siguiente - Usa anticipación negativa
(?!...)para coincidir "NO seguido de" - La retrospectiva tiene la misma lógica pero verifica lo que viene antes de la posición actual
¿Por qué mi expresión regular funciona en un probador pero no en TypeScript?
- Los probadores en línea pueden usar sabores diferentes de expresión regular (PCRE, Python, etc.) con características que JavaScript carece
- JavaScript no soporta cuantificadores posesivos (
a++), grupos atómicos ((?>...)), o alguna sintaxis PCRE - Las banderas
/s(dotall) y/d(indices) fueron agregadas en ES2018/ES2022 — ambientes antiguos pueden no soportarlas - Comprueba que estés usando las secuencias de escape correctas — en
new RegExp("\\d")necesitas barras invertidas dobles
¿Cómo debo tipificar resultados de expresión regular en TypeScript?
RegExp.exec()yString.match()devuelvenRegExpMatchArray | null— siempre null-verifica- Los grupos nombrados se tipifican como
Record<string, string>en la propiedadgroups— usa encadenamiento opcional - No hay forma integrada de tipificar nombres de grupo específicos — usa un contenedor o afirmación si es necesario
- Para
matchAll(), el tipo de devolución esIterableIterator<RegExpMatchArray>
¿Qué es la bandera /u y cuándo la necesito?
- La bandera
/uhabilita coincidencia Unicode completa — sin ella, la expresión regular trata pares sustitutos como dos caracteres - Se necesita cuando se coinciden emoji, caracteres CJK, o cualquier texto fuera del Plano Multilingual Básico
- Habilita escapes de propiedad Unicode
\p{...}como\p{Letter}o\p{Emoji} - Siempre usa
/ucuando trabajes con texto internacional para evitar errores sutiles de coincidencia de caracteres
¿Debo usar expresión regular para validación de correo electrónico?
- Una expresión regular básica como
/^[^\s@]+@[^\s@]+\.[^\s@]+$/atrapa la mayoría de errores tipográficos y está bien para UX - Ninguna expresión regular puede validar completamente un correo electrónico según RFC 5322 — la especificación es extremadamente compleja
- Para producción, combina una comprobación de expresión regular simple con un correo de confirmación o validación del lado del servidor
- Librerías como Zod proporcionan validadores
.email()que manejan casos extremos comunes
¿Cómo coincido entre múltiples líneas?
- Por defecto,
^y$coinciden con inicio/fin de toda la cadena - Usa la bandera
/m(multilínea) para hacer que^y$coincidan con inicio/fin de cada línea - Usa la bandera
/s(dotall) para hacer que.coincida con caracteres de salto de línea - Alternativamente, usa
[\s\S]en lugar de.para coincidir cualquier carácter incluyendo saltos de línea sin la bandera/s
¿Cuál es la diferencia entre replace() y replaceAll()?
replace()sin/greemplaza solo la primera coincidenciareplace()con/greemplaza todas las coincidenciasreplaceAll()siempre reemplaza todas las coincidencias — requiere bandera/gsi se pasa una expresión regularreplaceAll()con un argumento de cadena reemplaza todas las ocurrencias sin necesidad de expresión regularreplaceAll()fue agregado en ES2021 — usareplace(/pattern/g, ...)para ambientes antiguos
Relacionado
- TypeScript Basics — Conceptos core de TypeScript usados en tipificación de expresión regular
- Utility Types for React — Record y otros tipos usados en patrones de validador
- Type Narrowing — Comprobaciones null después de operaciones de coincidencia de expresión regular