`filter` chooses wrong type overload when used with `map`.
Closed this issue · 2 comments
This works:
const maybeStrings: (string | undefined)[] = ["a", undefined, "b"];
const strings: string[] = filter(isNotNil<string>)(maybeStrings);
But this doesn't, when involving a map
(which is an extremely common use case)
const arrayOfMaybeStringArrays: (string | undefined)[][] = [["a", undefined, "b"]];
const arrayOfStringArrays: string[] = map(filter(isNotNil<string>))(arrayOfMaybeStringArrays);
error TS2322: Type 'Dictionary<string>[]' is not assignable to type 'string[][]'.
Type 'Dictionary<string>' is missing the following properties from type 'string[]': length, pop, push, concat, and 35 more.
165 const arrayOfStringArrays: string[][] = map(filter(isNotNil<string>))(arrayOfMaybeStringArrays);
~~~~~~~~~~~~~~~~~~~
error TS2345: Argument of type '(string | undefined)[][]' is not assignable to parameter of type 'readonly Dictionary<string | null | undefined>[]'.
Type '(string | undefined)[]' is not assignable to type 'Dictionary<string | null | undefined>'.
Index signature for type 'string' is missing in type '(string | undefined)[]'.
165 const arrayOfStringArrays: string[][] = map(filter(isNotNil<string>))(arrayOfMaybeStringArrays);
~~~~~~~~~~~~~~~~~~~~~~~~
If you flip the order of the list
and dictionary
overrides for filter, like this, then the issue goes away:
export function filter<A, P extends A>(pred: (val: A) => val is P): {
<B extends A>(dict: Dictionary<B>): Dictionary<P>;
<B extends A>(list: readonly B[]): P[];
};
If this seems like a reasonable move, I can open a PR, just wanted to get a first impression from someone.
Correct that it should prioritize array over object. It's like that for map
as well. That fact that it's not is an oversight. I'll get to this as soon as I can
I realized while fixing this why the overload order is what it is. It's because for filter(pred)
, if you then pass it a readonly T[]
, the overload for Record<string, T>
actually picks it up for some weird reason, and we were getting incorrect return types for filter(pred)(list)
. But then you get the opposite problem with pipe(filter(pred))(list)
, in that it rejects list and wants a dict
Then I remembered we solved this for map
or ready by defining list
overload at both top and bottom, which makes everything work as expected!