Skip to content

Instantly share code, notes, and snippets.

@s-panferov
Created November 19, 2014 14:11
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save s-panferov/5269524dcf23dad9a1ef to your computer and use it in GitHub Desktop.
Save s-panferov/5269524dcf23dad9a1ef to your computer and use it in GitHub Desktop.
Result type in TypeScript
interface Result<T, E> {
map<U>(fn: (a: T) => U): Result<U, E>;
mapErr<U>(fn: (a: E) => U): Result<T, U>;
isOk(): boolean;
isErr(): boolean;
ok(): Option<T>;
err(): Option<E>;
and<U>(res: Result<U,E>): Result<U,E>;
andThen<U>(op: (T) => Result<U,E>): Result<U,E>;
or(res: Result<T,E>): Result<T,E>;
orElse<U>(op: (T) => Result<T,U>): Result<T,U>;
unwrap(): T;
unwrapOr(optb: T): T;
unwrapOrElse(op: (E) => T): T
}
class Ok<T, E> implements Result<T, E> {
private value: T;
constructor(v: T) {
this.value = v;
}
map <U>(fn: (a: T) => U): Result<U, E> {
return new Ok<U, E>(fn(this.value))
}
mapErr <U>(fn: (a: E) => U): Result<T, U> {
return new Ok<T, U>(this.value);
}
isOk(): boolean {
return true;
}
isErr(): boolean {
return false;
}
ok(): Option<T> {
return new Some(this.value);
}
err(): Option<E> {
return None.instance<E>();
}
and<U>(res: Result<U,E>): Result<U,E> {
return res;
}
andThen<U>(op: (T) => Result<U,E>): Result<U,E> {
return op(this.value);
}
or(res: Result<T,E>): Result<T,E> {
return this;
}
orElse<U>(op: (T) => Result<T,U>): Result<T,U> {
return new Ok<T,U>(this.value);
}
unwrapOr(optb: T): T {
return this.value;
}
unwrapOrElse(op: (E) => T): T {
return this.value;
}
unwrap(): T {
return this.value
}
toString(): string {
return "Some " + this.value;
}
}
class Err<T, E> implements Result<T, E> {
private error: E;
constructor(error: E) {
this.error = error;
}
map <U>(fn: (a: T) => U): Result<T, E> {
return this;
}
mapErr <U>(fn: (a: E) => U): Result<T, U> {
return new Err<T,U>(fn(this.error));
}
isOk(): boolean {
return false;
}
isErr(): boolean {
return false;
}
ok(): Option<T> {
return None.instance<T>();
}
err(): Option<E> {
return new Some(this.error);
}
and<U>(res: Result<U,E>): Result<U,E> {
return new Err<U,E>(this.error);
}
andThen<U>(op: (T) => Result<U,E>): Result<U,E> {
return new Err<U,E>(this.error);
}
or(res: Result<T,E>): Result<T,E> {
return res;
}
orElse<U>(op: (T) => Result<T,U>): Result<T,U> {
return op(this.error);
}
unwrapOr(optb: T): T {
return optb;
}
unwrapOrElse(op: (E) => T): T {
return op(this.error);
}
unwrap(): T {
throw "Err.get"
}
public toString(): string {
return "None";
}
}
@DanielRosenwasser
Copy link

Some of your functions are typed incorrectly - for instance, in andThen, you have a parameter named T instead of a parameter typed with T. In other words, you have

andThen<U>(op: (x: T) => Result<U,E>): Result<U,E>;

when it should be

andThen<U>(op: (x: T) => Result<U,E>): Result<U,E>;

Try using the --noImplicitAny flag to warn about these issues.

@qm3ster
Copy link

qm3ster commented Feb 5, 2019

@DanielRosenwasser Why does TypeScript interpret that as the name, though?
In type expression context, wouldn't optional name but mandatory type make a lot more sense?

@qm3ster
Copy link

qm3ster commented Feb 5, 2019

@s-panferov

interface BaseResult<T, E> {
  isOk(): this is Ok<T, E>
  isErr(): this is Err<T, E>
  ok(): Option<T>
  err(): Option<E>
  map<U>(fn: (val: T) => U): Result<U, E>
  mapErr<U>(fn: (err: E) => U): Result<T, U>
  and<U>(res: Result<U, E>): Result<U, E>
  andThen<U>(op: (val: T) => Result<U, E>): Result<U, E>
  or(res: Result<T, E>): Result<T, E>
  orElse<U>(op: (err: E) => Result<T, U>): Result<T, U>
  unwrap(): T | never
  unwrapOr(optb: T): T
  unwrapOrElse(op: (err: E) => T): T
}

type Result<T, E> = Ok<T, E> | Err<T, E>

class Ok<T, E> implements BaseResult<T, E> {
  constructor(private value: T) {}

  map<U>(fn: (a: T) => U) {
    return new Ok<U, E>(fn(this.value))
  }

  mapErr<U>(fn: (a: E) => U) {
    return (this as unknown) as Ok<T, U>
  }

  isOk(): this is Ok<T, E> {
    return true
  }

  isErr(): this is Err<T, E> {
    return false
  }

  ok(): Option<T> {
    return new Some(this.value)
  }

  err(): Option<E> {
    return None.instance<E>()
  }

  and<U>(res: Result<U, E>) {
    return res
  }

  andThen<U>(op: (val: T) => Result<U, E>) {
    return op(this.value)
  }

  or(res: Result<T, E>) {
    return this
  }

  orElse<U>(op: (err: E) => Result<T, U>) {
    return (this as unknown) as Ok<T, U>
  }

  unwrapOr(optb: T) {
    return this.value
  }

  unwrapOrElse(op: (err: E) => T) {
    return this.value
  }

  unwrap(): T {
    return this.value
  }

  toString() {
    return "Some " + this.value
  }
}

class Err<T, E> implements BaseResult<T, E> {
  constructor(private error: E) {}

  map<U>(fn: (a: T) => U) {
    return (this as unknown) as Err<U, E>
  }

  mapErr<U>(fn: (a: E) => U) {
    return new Err<T, U>(fn(this.error))
  }

  isOk(): this is Ok<T, E> {
    return false
  }

  isErr(): this is Err<T, E> {
    return false
  }

  ok(): Option<T> {
    return None.instance<T>()
  }

  err(): Option<E> {
    return new Some(this.error)
  }

  and<U>(res: Result<U, E>) {
    return (this as unknown) as Err<U, E>
  }

  andThen<U>(op: (val: T) => Result<U, E>) {
    return (this as unknown) as Err<U, E>
  }

  or(res: Result<T, E>) {
    return res
  }

  orElse<U>(op: (err: E) => Result<T, U>) {
    return op(this.error)
  }

  unwrapOr(optb: T) {
    return optb
  }

  unwrapOrElse(op: (err: E) => T) {
    return op(this.error)
  }

  unwrap(): never {
    throw this.error
  }

  toString() {
    return "None"
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment