/
Option.examples.spec.ts
123 lines (103 loc) · 4.24 KB
/
Option.examples.spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { expect, assert } from 'chai';
import { spy } from 'sinon';
import { Option, none, some} from '../src/Option';
describe('Use cases', () => {
describe('Example #1: Country code lookup', () => {
type Country = {name: string, code: number};
let countries: Country[] = [{
name: 'United States',
code: 1
}, {
name: 'United Kingdom',
code: 44
}];
describe('traditional way', () => {
// Traditionally we would try to find an item,
// check if it's not null or undefined and than
// get a property name.
function findCountryName(code: number): string {
const country = countries.find(c => c.code === code);
if (country) {
return country.name;
}
return 'Not found';
}
it('should return USA when search for code 1', () => {
expect(findCountryName(1)).to.be.eq('United States');
});
it('should return "Not found" when search for code -123', () => {
expect(findCountryName(-123)).to.be.eq('Not found');
});
});
describe('functional way', () => {
// Using option the step checking on null or undefined
// can be completely eliminated!
function findCountryName(code: number): string {
// no changes here
const country = countries.find(c => c.code === code);
// wrap a nullable item with an Option<A> using function `some()`
return some(country).map(_ => _.name).getOrElse('Not found');
}
it('should return USA when search for code 1', () => {
expect(findCountryName(1)).to.be.eq('United States');
});
it('should return "Not found" when search for code -123', () => {
expect(findCountryName(-123)).to.be.eq('Not found');
});
});
});
describe('Example #2: Users repository', () => {
class Repository<T> {
constructor(private readonly collection: T[]) {
}
find<K extends keyof T>(key: K, val: any): Option<T> {
return some(this.collection.find(_ => _[key] === val));
}
}
class Profile {
constructor(
public firstName: string,
public lastName: string,
public skill: Option<string> = none
) {
}
}
const greatFolksRepo = new Repository<Profile>([
new Profile('John', 'Lennon', some('Guitars')),
new Profile('Paul', 'McCartney', some('Lead Vocals')),
new Profile('George', 'Harrison', some('Lead guitar')),
new Profile('Ringo', 'Starr', some('Drums'))
]);
class Album {
constructor(
public title: string,
public year: number,
public author: string
) {
}
}
const greatMusicRepo = new Repository<Album>([
new Album('Ram', 197, 'Paul McCartney',)
]);
it('should find John Lennon skill', () => {
let skill = greatFolksRepo.find('firstName', 'John').flatMap(_ => _.skill);
expect(skill).to.be.deep.eq(some('Guitars'));
});
it('should gracefully handle not found person', () => {
let skill = greatFolksRepo.find('firstName', 'Dima').flatMap(_ => _.skill);
expect(skill).to.be.deep.eq(none);
});
it('should gracefully resolve options using `for` and do find a person and his great albums', () => {
const findAlbum = (firstName: string): string | undefined => {
for (let folk of greatFolksRepo.find('firstName', firstName)) {
let fullName = `${folk.firstName} ${folk.lastName}`;
for (let album of greatMusicRepo.find('author', fullName)) {
return album.title;
}
}
};
expect(findAlbum('Paul')).to.be.eq('Ram');
expect(findAlbum('Ringo')).to.be.undefined;
});
});
});