TypeScript实现类Rust Result类型及and方法类型推断问题排查
复刻Rust Result库时的TypeScript类型错误问题
问题重现
我尝试在TypeScript中复刻Rust的Result库,实现了以下代码:
原实现代码
interface BaseResult<T, E> { isOk(): boolean isErr(): boolean and<U>(res: Result<U, E>): Result<U, E> } class ErrImpl<E> implements BaseResult<never, E> { readonly val!: E constructor(val: E) { this.val = val } isOk(): boolean { return false } isErr(): boolean { return true } and<U>(res: Result<U, E>): Result<U, E> { return this } } class OkImpl<T> implements BaseResult<T, never> { readonly val!: T constructor(val: T) { this.val = val } isOk(): boolean { return true } isErr(): boolean { return false } and<U, E>(res: Result<U, E>) { return res } } export type Result<T, E> = OkImpl<T> | ErrImpl<E>
测试代码与错误
function alwaysFailsString(): Result<boolean, string> { return new ErrImpl("a") } let errString1 = alwaysFailsString() let errString2 = alwaysFailsString() let v = errString1.and(errString2)
出现类型错误:
This expression is not callable.
Each member of the union type '(<U, E>(res: Result<U, E>) => Result<U, E>) | (<U>(res: Result<U, string>) => Result<U, string>)' has signatures, but none of those signatures are compatible with each other.ts(2349)
我的预期是:and方法遵循Rust的逻辑——若当前是Ok则返回传入的res,若为Err则返回自身,类型上应该返回Result<U, E>。
错误原因
问题出在OkImpl的and方法泛型定义:
BaseResult接口中,and方法的泛型仅为<U>,E是当前BaseResult实例的错误类型(对于OkImpl来说,E是never)- 但
OkImpl中自行添加了泛型<U, E>,这相当于重新声明了一个任意的E类型,覆盖了接口继承的E类型 - 这导致
ErrImpl的and方法签名(固定使用自身的E)和OkImpl的and方法签名(任意E)无法兼容,TypeScript在处理联合类型的方法调用时,要求所有成员的方法签名必须可兼容,因此触发错误。
解决方案
修正OkImpl的and方法,移除多余的泛型参数E,让它遵循BaseResult接口的定义:
class OkImpl<T> implements BaseResult<T, never> { readonly val!: T constructor(val: T) { this.val = val } isOk(): boolean { return true } isErr(): boolean { return false } // 移除多余的E泛型,使用接口定义的E(即never) and<U>(res: Result<U, never>): Result<U, never> { return res } }
补充优化(可选)
如果希望OkImpl的and方法能接受任意错误类型的Result(更贴近Rust的行为,因为Rust中Ok的and会保留传入Result的错误类型),可以调整BaseResult的接口定义,让and方法的错误类型可以是原错误类型的超集:
interface BaseResult<T, E> { isOk(): boolean isErr(): boolean // 允许传入的Result错误类型是E的超集,返回的错误类型也对应调整 and<U, F extends E>(res: Result<U, F>): Result<U, F> } class ErrImpl<E> implements BaseResult<never, E> { readonly val!: E constructor(val: E) { this.val = val } isOk(): boolean { return false } isErr(): boolean { return true } and<U, F extends E>(res: Result<U, F>): Result<U, F> { return this } } class OkImpl<T> implements BaseResult<T, never> { readonly val!: T constructor(val: T) { this.val = val } isOk(): boolean { return true } isErr(): boolean { return false } and<U, F>(res: Result<U, F>): Result<U, F> { return res } }
这样调整后,OkImpl的and方法可以接受任意错误类型的Result,而ErrImpl的and方法仅接受错误类型是自身错误类型超集的Result,完全符合Rust中Result::and的行为。
内容的提问来源于stack exchange,提问作者Riccardo Perego
相关产品推荐
相关产品推荐

