TypeScript中如何实现模式匹配?有哪些推荐方案?
你提到的带tag的联合类型+switch/case确实是TypeScript里实现模式匹配的标准入门方案——毕竟TS本身没有原生的模式匹配语法,这种方式靠类型系统就能保证类型安全。不过确实,写多了switch会有点繁琐,这里给你几个更友好的方案,从原生优化到第三方工具都有:
1. 优化原生Switch方案:加上穷尽检查,避免遗漏Case
你的基础写法已经没问题,但可以加个穷尽检查来避免新增联合类型成员时忘记处理的问题。通过never类型的断言,TypeScript会在你漏写case时直接报错:
interface First { tag: 'one', value: number } interface Second { tag: 'two', text: string } type Third = First | Second function matchThird(input: Third): string { switch(input.tag) { case 'one': // TypeScript自动推断input为First类型,可直接访问value return `Number value: ${input.value}`; case 'two': return `Text value: ${input.text}`; default: // 新增Third成员时,input无法赋值给never,直接报错 const _exhaustiveCheck: never = input; throw new Error(`Unexpected tag: ${_exhaustiveCheck}`); } }
2. 封装函数式Match工具,让写法更声明式
如果你觉得switch不够优雅,可以自己封装一个通用的match函数,把各个tag的处理逻辑作为参数传入,写法更接近函数式的模式匹配:
// 定义匹配规则类型:每个tag对应一个处理函数 type MatchCases<T extends { tag: string }, R> = { [K in T['tag']]: (item: Extract<T, { tag: K }>) => R; } & { default?: (item: never) => R; }; // 通用match函数 function match<T extends { tag: string }, R>( input: T, cases: MatchCases<T, R> ): R { const handler = cases[input.tag as keyof typeof cases]; if (handler) { return handler(input as Extract<T, { tag: typeof input.tag }>); } if (cases.default) { return cases.default(input as never); } throw new Error(`No handler found for tag: ${input.tag}`); } // 使用示例 const testInput: Third = { tag: 'one', value: 42 }; const result = match(testInput, { one: (item) => `Got number: ${item.value}`, two: (item) => `Got text: ${item.text}` });
这种方式不用写重复的switch,逻辑更紧凑,而且同样能享受TypeScript的类型推断。
3. 类型层面的模式匹配:用条件类型处理类型转换
如果你的需求是类型层面的模式匹配(比如根据输入类型推导输出类型),可以用TypeScript的条件类型来实现,这在类型工具函数里很常用:
interface First { tag: 'one' } interface Second { tag: 'two' } type Third = First | Second // 根据tag匹配对应的类型 type MatchResult<T> = T extends { tag: 'one' } ? number : T extends { tag: 'two' } ? string : never; // 测试:TypeScript自动推导结果类型 type Result1 = MatchResult<First>; // number type Result2 = MatchResult<Second>; // string
你可以把这种类型层面的匹配和运行时逻辑结合,让类型和代码更一致。
4. 第三方库:用ts-pattern实现接近原生的模式匹配
如果你的场景比较复杂(比如嵌套结构、数组模式、通配符匹配),推荐用专门的模式匹配库ts-pattern,它的语法接近Rust、Scala这类语言的原生模式匹配,功能强大且类型安全:
import { match, P } from 'ts-pattern'; // 支持嵌套结构匹配 interface NestedFirst { tag: 'one', data: { id: number } } interface NestedSecond { tag: 'two', data: { name: string } } type NestedThird = NestedFirst | NestedSecond; const result = match(someNestedValue) // 匹配tag为'one'且data.id大于10的情况 .with({ tag: 'one', data: { id: P.gt(10) } }, (item) => `Large ID: ${item.data.id}`) .with({ tag: 'two' }, (item) => `Name: ${item.data.name}`) // 穷尽检查,漏写case会报错 .exhaustive();
这个库还支持数组解构、可选值匹配、通配符等高级特性,适合中大型项目使用。
内容的提问来源于stack exchange,提问作者Jimi Pajala
相关产品推荐
相关产品推荐

