From f29d7df5d150070eb0e3e4cf49d76560d72ea63b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 24 May 2017 15:50:30 -0700 Subject: [PATCH] Add tests --- .../inferFromGenericFunctionReturnTypes1.ts | 70 ++++++++++++++ .../inferFromGenericFunctionReturnTypes2.ts | 94 +++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts create mode 100644 tests/cases/compiler/inferFromGenericFunctionReturnTypes2.ts diff --git a/tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts b/tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts new file mode 100644 index 00000000000..3fb454ec7e5 --- /dev/null +++ b/tests/cases/compiler/inferFromGenericFunctionReturnTypes1.ts @@ -0,0 +1,70 @@ +// Repro from #15680 + +// This is a contrived class. We could do the same thing with Observables, etc. +class SetOf { + _store: A[]; + + add(a: A) { + this._store.push(a); + } + + transform(transformer: (a: SetOf) => SetOf): SetOf { + return transformer(this); + } + + forEach(fn: (a: A, index: number) => void) { + this._store.forEach((a, i) => fn(a, i)); + } +} + +function compose( + fnA: (a: SetOf) => SetOf, + fnB: (b: SetOf) => SetOf, + fnC: (c: SetOf) => SetOf, + fnD: (c: SetOf) => SetOf, +):(x: SetOf) => SetOf; +/* ... etc ... */ +function compose(...fns: ((x: T) => T)[]): (x: T) => T { + return (x: T) => fns.reduce((prev, fn) => fn(prev), x); +} + +function map(fn: (a: A) => B): (s: SetOf) => SetOf { + return (a: SetOf) => { + const b: SetOf = new SetOf(); + a.forEach(x => b.add(fn(x))); + return b; + } +} + +function filter(predicate: (a: A) => boolean): (s: SetOf) => SetOf { + return (a: SetOf) => { + const result = new SetOf(); + a.forEach(x => { + if (predicate(x)) result.add(x); + }); + return result; + } +} + +const testSet = new SetOf(); +testSet.add(1); +testSet.add(2); +testSet.add(3); + +testSet.transform( + compose( + filter(x => x % 1 === 0), + map(x => x + x), + map(x => x + '!!!'), + map(x => x.toUpperCase()) + ) +) + +testSet.transform( + compose( + filter(x => x % 1 === 0), + map(x => x + x), + map(x => 123), // Whoops a bug + map(x => x.toUpperCase()) // causes an error! + ) +) diff --git a/tests/cases/compiler/inferFromGenericFunctionReturnTypes2.ts b/tests/cases/compiler/inferFromGenericFunctionReturnTypes2.ts new file mode 100644 index 00000000000..314159d363c --- /dev/null +++ b/tests/cases/compiler/inferFromGenericFunctionReturnTypes2.ts @@ -0,0 +1,94 @@ +type Mapper = (x: T) => U; + +declare function wrap(cb: Mapper): Mapper; + +declare function arrayize(cb: Mapper): Mapper; + +declare function combine(f: (x: A) => B, g: (x: B) => C): (x: A) => C; + +declare function foo(f: Mapper): void; + +let f1: Mapper = s => s.length; +let f2: Mapper = wrap(s => s.length); +let f3: Mapper = arrayize(wrap(s => s.length)); +let f4: Mapper = combine(wrap(s => s.length), wrap(n => n >= 10)); + +foo(wrap(s => s.length)); + +let a1 = ["a", "b"].map(s => s.length); +let a2 = ["a", "b"].map(wrap(s => s.length)); +let a3 = ["a", "b"].map(wrap(arrayize(s => s.length))); +let a4 = ["a", "b"].map(combine(wrap(s => s.length), wrap(n => n > 10))); +let a5 = ["a", "b"].map(combine(identity, wrap(s => s.length))); +let a6 = ["a", "b"].map(combine(wrap(s => s.length), identity)); + +// This is a contrived class. We could do the same thing with Observables, etc. +class SetOf { + _store: A[]; + + add(a: A) { + this._store.push(a); + } + + transform(transformer: (a: SetOf) => SetOf): SetOf { + return transformer(this); + } + + forEach(fn: (a: A, index: number) => void) { + this._store.forEach((a, i) => fn(a, i)); + } +} + +function compose( + fnA: (a: SetOf) => SetOf, + fnB: (b: SetOf) => SetOf, + fnC: (c: SetOf) => SetOf, + fnD: (c: SetOf) => SetOf, +):(x: SetOf) => SetOf; +/* ... etc ... */ +function compose(...fns: ((x: T) => T)[]): (x: T) => T { + return (x: T) => fns.reduce((prev, fn) => fn(prev), x); +} + +function map(fn: (a: A) => B): (s: SetOf) => SetOf { + return (a: SetOf) => { + const b: SetOf = new SetOf(); + a.forEach(x => b.add(fn(x))); + return b; + } +} + +function filter(predicate: (a: A) => boolean): (s: SetOf) => SetOf { + return (a: SetOf) => { + const result = new SetOf(); + a.forEach(x => { + if (predicate(x)) result.add(x); + }); + return result; + } +} + +const testSet = new SetOf(); +testSet.add(1); +testSet.add(2); +testSet.add(3); + +const t1 = testSet.transform( + compose( + filter(x => x % 1 === 0), + map(x => x + x), + map(x => x + '!!!'), + map(x => x.toUpperCase()) + ) +) + +declare function identity(x: T): T; + +const t2 = testSet.transform( + compose( + filter(x => x % 1 === 0), + identity, + map(x => x + '!!!'), + map(x => x.toUpperCase()) + ) +)