Skip to content

Commit

Permalink
feat(Result): Result is a Functor now
Browse files Browse the repository at this point in the history
  • Loading branch information
joanllenas committed May 18, 2019
1 parent d7b9e8b commit 356c3b0
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 13 deletions.
9 changes: 3 additions & 6 deletions src/json-decoder.spec.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { JsonDecoder, $JsonDecoderErrors } from './json-decoder';

import * as chai from 'chai';
import { Ok, Result, Err } from './result';
import { Ok, Result, Err, ok, err } from './result';

const expect = chai.expect;

// Test utils
const expectOkWithValue = <a>(result: Result<a>, expectedValue: a) =>
expect(result)
.to.be.an.instanceof(Ok)
.and.to.deep.equal({
value: expectedValue,
map: Ok.prototype.map
});
.and.to.deep.equal(ok(expectedValue));
const expectErr = <a>(result: Result<a>) =>
expect(result).to.be.an.instanceof(Err);
const expectErrWithMsg = <a>(result: Result<a>, expectedErrorMsg: string) =>
expect(result)
.to.be.an.instanceof(Err)
.and.to.deep.equal({ error: expectedErrorMsg, map: Err.prototype.map });
.and.to.deep.equal(err(expectedErrorMsg));

// Tests
describe('json-decoder', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/json-decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export namespace JsonDecoder {
if (result instanceof Ok) {
return ok(fn(result.value));
} else {
return result;
return err(result.error);
}
});
}
Expand All @@ -71,7 +71,7 @@ export namespace JsonDecoder {
if (result instanceof Ok) {
return fn(result.value).decode(json);
} else {
return result;
return err(result.error);
}
});
}
Expand Down
49 changes: 49 additions & 0 deletions src/result.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { JsonDecoder, $JsonDecoderErrors } from './json-decoder';

import * as chai from 'chai';
import { Ok, ok, err } from './result';

const expect = chai.expect;

describe('Result', () => {
const toUpper = (str: string) => str.toUpperCase();
const exclamation = (str: string) => `${str}!`;

describe('Ok', () => {
it('should wrap the value', () => {
expect(new Ok('hola').value).to.equal('hola');
});
it('should chain transformations while mapping', () => {
expect(
ok('hola')
.map(toUpper)
.map(exclamation)
).to.deep.equal(ok('HOLA!'));
});
});

describe('Err', () => {
it('should wrap the error', () => {
expect(err('Wrong!')).to.deep.equal(err('Wrong!'));
});
it('should keep the first error while mapping', () => {
expect(
err<string>('Wrong')
.map(toUpper)
.map(exclamation)
).to.deep.equal(err('Wrong'));
});
});

it('should allow chaining more than one transformation', () => {
const onlyEven = (num: number) =>
num % 2 === 0 ? ok(num) : err<number>('not even');
const example = (n: number) =>
onlyEven(n)
.map(n => n * 2)
.map(n => Number(n).toString(16))
.map(hex => `#${hex}`);
expect(example(5)).to.deep.equal(err('not even'));
expect(example(6)).to.deep.equal(ok('#c'));
});
});
12 changes: 7 additions & 5 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ export class Ok<a> {
return ok(fn(this.value));
}
}
export class Err {

export class Err<a> {
constructor(readonly error: string) {}

map<b>(fn: (a: any) => b): Result<b> {
return this;
map<b>(fn: (a: a) => b): Result<b> {
return err<b>(this.error);
}
}
export type Result<a> = Ok<a> | Err;

export type Result<a> = Ok<a> | Err<a>;

export function ok<a>(value: a): Result<a> {
return new Ok(value);
}

export function err<a>(error: string): Result<a> {
return new Err(error);
return new Err<a>(error);
}

0 comments on commit 356c3b0

Please sign in to comment.