如何用TypeScript约束字符串为合法邮箱格式?
实现编译时+运行时双重验证的邮箱字符串类型
要实现既能约束邮箱格式、又能直接当作原始字符串使用的类型,可以结合TypeScript的品牌类型(Branded Types)和模板字面量类型,配合验证函数来完成。
1. 定义带格式约束的品牌类型
首先定义Email类型,用模板字面量做基础格式检查,同时添加品牌标记让TypeScript识别这是经过验证的邮箱:
// 基础格式约束:必须包含@和.,且前后有内容 type Email = `${string}@${string}.${string}` & { __brand: 'Email' };
这里的__brand是类型层面的标记,运行时不会存在,所以Email本质还是字符串类型,能直接使用所有字符串方法。
2. 编写运行时验证函数
用正则实现严谨的邮箱格式验证,同时用类型谓词让TypeScript识别验证通过的字符串为Email类型:
// 运行时邮箱验证正则(覆盖大部分合法邮箱格式) function isValidEmail(str: string): str is Email { const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; return emailRegex.test(str); }
3. 工厂函数创建验证后的邮箱
通过工厂函数统一处理邮箱创建,确保只有验证通过的字符串才能被赋值给Email类型变量:
function createEmail(str: string): Email { if (!isValidEmail(str)) { throw new Error('无效的邮箱格式'); } // 断言为Email类型,让TypeScript认可这是合法邮箱 return str as Email; }
4. 使用示例
let email: Email; // 直接赋值无效字面量 → 编译报错 email = "myEmail"; // ❌ 编译错误:类型 '"myEmail"' 无法赋值给类型 'Email' // 直接赋值有效字面量 → 编译通过 email = "myEmail@stackoverflow.com"; // ✅ 编译通过 // 处理动态输入(比如用户输入) const userInput = "invalid-email"; // email = userInput; // ❌ 编译错误:类型 'string' 无法赋值给类型 'Email' try { email = createEmail(userInput); // ❌ 运行时抛出错误:无效的邮箱格式 } catch (e) { console.log((e as Error).message); } // 验证通过的邮箱可直接当作字符串使用 email = createEmail("myEmail@stackoverflow.com"); console.log(email); // 输出:myEmail@stackoverflow.com console.log(email.includes("@")); // 输出:true console.log(email === "myEmail@stackoverflow.com"); // 输出:true
说明
- 编译时:模板字面量会检查字面量的基础格式,动态字符串必须通过
createEmail才能赋值给Email类型,否则TypeScript直接报错。 - 运行时:
createEmail里的正则验证会拦截所有无效格式的邮箱,确保运行时不会出现非法值。 - 易用性:
Email类型本质还是字符串,不需要像包装类那样通过value属性访问,直接调用字符串方法、打印、比较都和普通字符串完全一致。
内容的提问来源于stack exchange,提问作者Rolazar
相关产品推荐
相关产品推荐

