From b8b95c0cf836a44add19524f7deb63cbaba182e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miika=20H=C3=A4nninen?= Date: Tue, 2 Oct 2018 21:34:46 +0300 Subject: [PATCH] ramda: Workaround for type inference problem in Filter (#28592) * ramda: add return type assertions for filter/reject test * ramda: Add failing test for filter-in-pipe scenario * ramda: Workaround for type inference problem in Filter In the `Filter` type, when used inside a pipe, the second format always took precedence in type inference, causing problems when a list was filtered. With this change, you can manually specify whether you are using filter for an array or an object, so usage in a pipe will work. You can also leave out the second type parameter, in which case filter works as previously. ```ts import { pipe, filter } from 'ramda'; pipe( filter((val: string) => val.length > 2), )(['list', 'or', 'object']); pipe( filter((val) => val.length > 2), )({ a: 'list', b: 'or', c: 'object' }); ``` --- types/ramda/index.d.ts | 22 +++++++++++++--------- types/ramda/ramda-tests.ts | 30 ++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/types/ramda/index.d.ts b/types/ramda/index.d.ts index 05e84e721f..9623aa8865 100644 --- a/types/ramda/index.d.ts +++ b/types/ramda/index.d.ts @@ -80,9 +80,17 @@ declare namespace R { set(str: string, obj: T): U; } - interface Filter { - (list: ReadonlyArray): T[]; - (obj: Dictionary): Dictionary; + interface Filter { + (fn: (value: T) => boolean): FilterOnceApplied; + (fn: (value: T) => boolean): (list: ReadonlyArray) => T[]; + (fn: (value: T) => boolean): (list: Dictionary) => Dictionary; + (fn: (value: T) => boolean, list: ReadonlyArray): T[]; + (fn: (value: T) => boolean, obj: Dictionary): Dictionary; + } + + interface FilterOnceApplied { + (list: ReadonlyArray): T[]; + (obj: Dictionary): Dictionary; } type Evolve, E extends Evolver> = { @@ -721,9 +729,7 @@ declare namespace R { /** * Returns a new list containing only those items that match a given predicate function. The predicate function is passed one argument: (value). */ - filter(fn: (value: T) => boolean): Filter; - filter(fn: (value: T) => boolean, list: ReadonlyArray): T[]; - filter(fn: (value: T) => boolean, obj: Dictionary): Dictionary; + filter: Filter; /** * Returns the first element of the list which matches the predicate, or `undefined` if no @@ -1906,9 +1912,7 @@ declare namespace R { * Similar to `filter`, except that it keeps only values for which the given predicate * function returns falsy. */ - reject(fn: (value: T) => boolean): Filter; - reject(fn: (value: T) => boolean, list: ReadonlyArray): T[]; - reject(fn: (value: T) => boolean, obj: Dictionary): Dictionary; + reject: Filter; /** * Removes the sub-list of `list` starting at index `start` and containing `count` elements. diff --git a/types/ramda/ramda-tests.ts b/types/ramda/ramda-tests.ts index 73ba908fd5..951c11ba02 100644 --- a/types/ramda/ramda-tests.ts +++ b/types/ramda/ramda-tests.ts @@ -695,12 +695,34 @@ R.times(i, 5); } const filterEven = R.filter(isEven); - filterEven({ a: 0, b: 1 }); // => { a: 0 } - filterEven([0, 1]); // => [0] + const objA: R.Dictionary = filterEven({ a: 0, b: 1 }); // => { a: 0 } + const listA: number[] = filterEven([0, 1]); // => [0] const rejectEven = R.reject(isEven); - rejectEven({ a: 0, b: 1 }); // => { b: 1 } - rejectEven([0, 1]); // => [1] + const objB: R.Dictionary = rejectEven({ a: 0, b: 1 }); // => { b: 1 } + const listB: number[] = rejectEven([0, 1]); // => [1] +}; + +() => { + function isEven(n: number) { + return n % 2 === 0; + } + + const a: R.Dictionary = R.pipe( + R.filter(isEven), + )({ a: 0, b: 1 }); // => { a: 0 } + + const b: number[] = R.pipe( + R.filter(isEven), + )([0, 1]); // => [0] + + const c: R.Dictionary = R.pipe( + R.reject(isEven), + )({ a: 0, b: 1 }); // => { b: 1 } + + const d: number[] = R.pipe( + R.reject(isEven), + )([0, 1]); // => [1] }; () => {