TypeScript中基于联合类型子集的match函数类型优化
为ADT默认匹配器推导精确参数类型的TypeScript技巧
背景与问题
我正在开发一个TypeScript代数数据类型(ADT)库,核心功能是创建ADT实例并通过类Rust风格的match函数做模式匹配。目前在默认分支的参数类型推导上遇到了问题。
ADT基础使用方式:
// 创建ADT实例生成器 const value_adt = adt({ num: (value: number) => value, str: (value: string) => value, obj: (value: object) => value, }); // 提取变体联合类型 type ValueADT = Variants<typeof value_adt>; // ValueADT 会被推导为: // { tag: "num", value: number } | // { tag: "str", value: string } | // { tag: "obj", value: object } // 创建ADT实例 const my_value = value_adt.str("hello") as ValueADT;
match函数常规用法:
match(my_value, { num: value => console.log(`number: ${value}`), str: value => console.log(`string: ${value}`), obj: value => console.log(`object: ${value}`), });
我为match添加了默认分支选项,允许省略部分匹配器:
match(my_value, { num: value => console.log(`number: ${value}`), [def]: value => console.log(`not number: ${value}`), });
当前默认匹配器的参数类型是所有变体值的联合(如number | string | object),但实际场景中,默认分支只会处理未被显式匹配的变体——上述示例中应该是string | object。我需要用TypeScript高级类型技巧,让默认匹配器的参数类型根据已提供的匹配器自动推导剩余变体的值类型。
问题复现代码
以下是简化后的独立代码,当前默认匹配器参数包含了逻辑上不可能的number类型:
type Variant<T, U> = { tag: T, value: U }; const adt = { tag: "num", value: 1 } as | Variant<"num", number> | Variant<"str", string> | Variant<"obj", object>; type MatchAll<T extends Variant<string, any>> = { [K in T["tag"]]: T extends Variant<K, infer V> ? (value: V) => any : never }; const def = Symbol("[def]ault matcher"); type Matchers<T extends Variant<string, any>> = | MatchAll<T> | Partial<MatchAll<T>> & { [def]: (value: T["value"]) => any; }; function match< T extends Variant<string, any>, U extends Matchers<T>, >( variant: T, matchers: U, ) { const matcher = matchers[variant.tag as keyof U]; if (matcher === undefined) { throw new Error(`No match for '${variant.tag}'!`); } matcher(variant.value); } match(adt, { num: value => console.log(`number: ${value}`), [def]: value => console.log(`not number: ${value}`), });
问题解决更新
问题解决后,我进一步实现了match函数返回值的正确类型推导,让返回类型自动合并所有匹配器返回值的联合类型。
内容的提问来源于stack exchange,提问作者Ola Næss Kaldestad
相关产品推荐
相关产品推荐

