如何为字符串模板字面量创建类型守卫并区分联合类型?
区分TypeScript模板字面量联合类型的正确方式
问题背景
当定义Url = https://${string}或`ID = `${string}_id这类模板字面量联合类型后,直接使用startsWith或includes等运行时字符串检查无法让TypeScript自动收窄类型,导致传入对应类型的函数时出现类型不兼容错误。
解决方案:自定义类型守卫函数
TypeScript无法自动将普通字符串操作与模板字面量类型关联,需要手动编写类型守卫函数,明确告诉编译器运行时检查通过后变量的具体类型。
定义类型守卫
为每个模板字面量类型编写返回val is [Type]的守卫函数:
function isUrl(val: string): val is Url { return val.startsWith("https://"); } function isID(val: string): val is ID { return val.endsWith("_id"); }
修正后的完整代码
type Url = `https://${string}`; type ID = `${string}_id`; type AttributeType = number | Date | Url | ID; const handleNumber = (val: number) => console.log(`${val}: is a number!`) const handleDate = (val: Date) => console.log(`${val}: is a date!`) const handleUrl = (val: Url) => console.log(`${val}: is a url!`) const handleID = (val: ID) => console.log(`${val}: is an id!`) // 自定义类型守卫函数 function isUrl(val: string): val is Url { return val.startsWith("https://"); } function isID(val: string): val is ID { return val.endsWith("_id"); } function AttributeLabel(attribute: AttributeType) { if (typeof attribute === "number") { handleNumber(attribute); } else if (attribute instanceof Date) { handleDate(attribute); } else if (typeof attribute === "string" && isUrl(attribute)) { handleUrl(attribute); // 类型成功收窄为Url,无报错 } else if (typeof attribute === "string" && isID(attribute)) { handleID(attribute); // 类型成功收窄为ID,无报错 } }; // 测试输出 console.log(AttributeLabel(1)) // 1 is a number! console.log(AttributeLabel(new Date())) // [当前日期] is a date! console.log(AttributeLabel("https://google.com")) // https://google.com is a url! console.log(AttributeLabel("dog_id")) // dog_id is an id!
原理说明
类型守卫函数通过返回val is Type的类型谓词,让TypeScript编译器知道:当函数返回true时,变量val的类型就是指定的模板字面量类型。这就把运行时的字符串检查和编译时的类型推断关联起来,实现了准确的类型收窄。
内容的提问来源于stack exchange,提问作者JeremiahDuane
相关产品推荐
相关产品推荐

