Skip to content

Commit f29d7df

Browse files
committed
Add tests
1 parent b8d5eff commit f29d7df

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Repro from #15680
2+
3+
// This is a contrived class. We could do the same thing with Observables, etc.
4+
class SetOf<A> {
5+
_store: A[];
6+
7+
add(a: A) {
8+
this._store.push(a);
9+
}
10+
11+
transform<B>(transformer: (a: SetOf<A>) => SetOf<B>): SetOf<B> {
12+
return transformer(this);
13+
}
14+
15+
forEach(fn: (a: A, index: number) => void) {
16+
this._store.forEach((a, i) => fn(a, i));
17+
}
18+
}
19+
20+
function compose<A, B, C, D, E>(
21+
fnA: (a: SetOf<A>) => SetOf<B>,
22+
fnB: (b: SetOf<B>) => SetOf<C>,
23+
fnC: (c: SetOf<C>) => SetOf<D>,
24+
fnD: (c: SetOf<D>) => SetOf<E>,
25+
):(x: SetOf<A>) => SetOf<E>;
26+
/* ... etc ... */
27+
function compose<T>(...fns: ((x: T) => T)[]): (x: T) => T {
28+
return (x: T) => fns.reduce((prev, fn) => fn(prev), x);
29+
}
30+
31+
function map<A, B>(fn: (a: A) => B): (s: SetOf<A>) => SetOf<B> {
32+
return (a: SetOf<A>) => {
33+
const b: SetOf<B> = new SetOf();
34+
a.forEach(x => b.add(fn(x)));
35+
return b;
36+
}
37+
}
38+
39+
function filter<A>(predicate: (a: A) => boolean): (s: SetOf<A>) => SetOf<A> {
40+
return (a: SetOf<A>) => {
41+
const result = new SetOf<A>();
42+
a.forEach(x => {
43+
if (predicate(x)) result.add(x);
44+
});
45+
return result;
46+
}
47+
}
48+
49+
const testSet = new SetOf<number>();
50+
testSet.add(1);
51+
testSet.add(2);
52+
testSet.add(3);
53+
54+
testSet.transform(
55+
compose(
56+
filter(x => x % 1 === 0),
57+
map(x => x + x),
58+
map(x => x + '!!!'),
59+
map(x => x.toUpperCase())
60+
)
61+
)
62+
63+
testSet.transform(
64+
compose(
65+
filter(x => x % 1 === 0),
66+
map(x => x + x),
67+
map(x => 123), // Whoops a bug
68+
map(x => x.toUpperCase()) // causes an error!
69+
)
70+
)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
type Mapper<T, U> = (x: T) => U;
2+
3+
declare function wrap<T, U>(cb: Mapper<T, U>): Mapper<T, U>;
4+
5+
declare function arrayize<T, U>(cb: Mapper<T, U>): Mapper<T, U[]>;
6+
7+
declare function combine<A, B, C>(f: (x: A) => B, g: (x: B) => C): (x: A) => C;
8+
9+
declare function foo(f: Mapper<string, number>): void;
10+
11+
let f1: Mapper<string, number> = s => s.length;
12+
let f2: Mapper<string, number> = wrap(s => s.length);
13+
let f3: Mapper<string, number[]> = arrayize(wrap(s => s.length));
14+
let f4: Mapper<string, boolean> = combine(wrap(s => s.length), wrap(n => n >= 10));
15+
16+
foo(wrap(s => s.length));
17+
18+
let a1 = ["a", "b"].map(s => s.length);
19+
let a2 = ["a", "b"].map(wrap(s => s.length));
20+
let a3 = ["a", "b"].map(wrap(arrayize(s => s.length)));
21+
let a4 = ["a", "b"].map(combine(wrap(s => s.length), wrap(n => n > 10)));
22+
let a5 = ["a", "b"].map(combine(identity, wrap(s => s.length)));
23+
let a6 = ["a", "b"].map(combine(wrap(s => s.length), identity));
24+
25+
// This is a contrived class. We could do the same thing with Observables, etc.
26+
class SetOf<A> {
27+
_store: A[];
28+
29+
add(a: A) {
30+
this._store.push(a);
31+
}
32+
33+
transform<B>(transformer: (a: SetOf<A>) => SetOf<B>): SetOf<B> {
34+
return transformer(this);
35+
}
36+
37+
forEach(fn: (a: A, index: number) => void) {
38+
this._store.forEach((a, i) => fn(a, i));
39+
}
40+
}
41+
42+
function compose<A, B, C, D, E>(
43+
fnA: (a: SetOf<A>) => SetOf<B>,
44+
fnB: (b: SetOf<B>) => SetOf<C>,
45+
fnC: (c: SetOf<C>) => SetOf<D>,
46+
fnD: (c: SetOf<D>) => SetOf<E>,
47+
):(x: SetOf<A>) => SetOf<E>;
48+
/* ... etc ... */
49+
function compose<T>(...fns: ((x: T) => T)[]): (x: T) => T {
50+
return (x: T) => fns.reduce((prev, fn) => fn(prev), x);
51+
}
52+
53+
function map<A, B>(fn: (a: A) => B): (s: SetOf<A>) => SetOf<B> {
54+
return (a: SetOf<A>) => {
55+
const b: SetOf<B> = new SetOf();
56+
a.forEach(x => b.add(fn(x)));
57+
return b;
58+
}
59+
}
60+
61+
function filter<A>(predicate: (a: A) => boolean): (s: SetOf<A>) => SetOf<A> {
62+
return (a: SetOf<A>) => {
63+
const result = new SetOf<A>();
64+
a.forEach(x => {
65+
if (predicate(x)) result.add(x);
66+
});
67+
return result;
68+
}
69+
}
70+
71+
const testSet = new SetOf<number>();
72+
testSet.add(1);
73+
testSet.add(2);
74+
testSet.add(3);
75+
76+
const t1 = testSet.transform(
77+
compose(
78+
filter(x => x % 1 === 0),
79+
map(x => x + x),
80+
map(x => x + '!!!'),
81+
map(x => x.toUpperCase())
82+
)
83+
)
84+
85+
declare function identity<T>(x: T): T;
86+
87+
const t2 = testSet.transform(
88+
compose(
89+
filter(x => x % 1 === 0),
90+
identity,
91+
map(x => x + '!!!'),
92+
map(x => x.toUpperCase())
93+
)
94+
)

0 commit comments

Comments
 (0)