traverse1984 / oxide.ts

Rust's Option<T> and Result<T, E>, implemented for TypeScript.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

flatten() for Result and Option and nested Ok()'s

rrichardson opened this issue · comments

My hope is to copy these methods from Rust: (https://doc.rust-lang.org/src/core/result.rs.html#1704)

I attempted to implement these, but alas, Typescripts type system is beyond my reckoning.
My first approach was a naive one, my expectation was that this would work, though the type error would not be ergonomic if flatten() was called on a non-nested result.

flatten(this: Result<Result<T, E>, E>): Result<T, E> {
 return this.andThen((x) => x)
}

When trying to test flatten, I get the unergonomic error message that I expected, but not for the reason I expected. I guess Ok doesn't implicitly convert to Result..

  expect(Ok(Ok(1)).flatten()).to.be.true; 

results in

The 'this' context of type 'Ok<Err<string>>' is not assignable to method's 'this' of type 'Result<Result<Err<string>, never>, never>'.
  Type 'Err<string>' is not assignable to type 'Result<Err<string>, never>'.
    Type 'string' is not assignable to type 'never'. 

Cool idea. Where were you two weeks ago!

I think the issue here (I'm not at an IDE to check) is that the T type should be the contained type of the Result. The outer Ok has T = Ok<number> and your definition asks that this be Result<Result<T,_>> which it never can be.

You will need to infer the type of the contained T, perhaps something like:

flatten(this: Result<T, E>): T extends Result<infer U, E> ? Result<U, E> : null {}

The null here indicates the instance where it's not a nested result, up to you how you want to handle that. I haven't tested this, but maybe it works for what you want?

Figured it out.. it just needed a new set of type params for the flatten method itself, as T and E don't equal Result<T, E>

#6

I think the other approach, which is more closely related to your example, is :

   flatten<T, U extends Result<T, E>>(this: Result<U, E>): Result<T, E> {
      return this.andThen(x => x)
   }

I'm not sure which one is better. I went with the flatten<T, E> cause it's simple.