为何我的其中一个compose函数无法正确推导类型?
函数组合类型推导问题:compose 与 composeR 的差异及修复
问题背景
定义了两个函数组合工具compose和composeR,二者逻辑完全一致,仅参数顺序相反:将一个函数的输出作为另一个函数的输入完成组合。在组合简单的数值递增、转字符串函数时,二者的类型推导均正常;但组合Option Functor和Either Functor的map方法时,compose出现类型推导错误,composeR却能正常工作。
相关代码示例:
// compose与composeR定义 type Compose = <A, B, C>(f: (x: B) => C, g: (x: A) => B) => (x: A) => C const compose: Compose = (f, g) => x => f(g(x)) type ComposeR = <A, B, C>(g: (x: A) => B, f: (x: B) => C) => (x: A) => C const composeR: ComposeR = (g, f) => x => f(g(x)) // Option与Either的Functor实现 const optionFunctor: Functor1<'Option'> = { /* ... */ } const eitherFunctor: Functor2<'Either'> = { /* ... */ } // 组合map函数时的错误场景 const map1 = optionFunctor.map const map2 = eitherFunctor.map const composed1 = compose(map1, map2) // 类型推导错误 const composed2 = composeR(map1, map2) // 类型推导正常
问题原因
核心在于TypeScript的类型推导顺序与函数参数顺序强相关:
compose的参数顺序是(f: (B) => C, g: (A) => B),TypeScript会优先推导第一个传入的f(即optionFunctor.map)的类型。但Functor的map是多态高阶函数(需要上下文确定输入输出类型),此时没有足够信息确定B的类型,导致后续g的类型无法与f匹配,最终推导失败。composeR的参数顺序是(g: (A) => B, f: (B) => C),先传入的g(即eitherFunctor.map)会被优先推导,确定A和B的类型后,第二个参数f的类型可以基于已确定的B完成匹配,因此推导顺利完成。
补充说明:Option的map类型大致为<A, B>(fn: (a: A) => B) => (o: Option<A>) => Option<B>,Either的map类型大致为<E, A, B>(fn: (a: A) => B) => (e: Either<E, A>) => Either<E, B>,这类多态函数的类型推导严重依赖上下文参数的顺序。
修复方案
1. 调整compose的参数顺序(推荐)
直接将compose的参数顺序改为与composeR一致,让类型推导逻辑自然对齐:
type Compose = <A, B, C>(g: (x: A) => B, f: (x: B) => C) => (x: A) => C const compose: Compose = (g, f) => x => f(g(x))
2. 显式指定类型参数(临时 workaround)
如果不想修改参数顺序,可以在调用compose时显式指定类型参数,帮TypeScript明确类型关系:
// 需根据实际场景替换E、A、B为具体类型 const composed1 = compose<Either<E, A>, Option<A>, Option<B>>(map1, map2)
3. 优化compose的类型参数顺序
调整类型参数的声明顺序,让TypeScript优先推导两个函数的重叠类型B,再推导A和C,部分场景下可自动完成推导:
type Compose = <B, C, A>(f: (x: B) => C, g: (x: A) => B) => (x: A) => C const compose: Compose = (f, g) => x => f(g(x))
内容的提问来源于stack exchange,提问作者Shnd
相关产品推荐
相关产品推荐

