javascripttypescriptramda.jscurrying

When generic and ramda library's curry() are used together, generic is not applied


This is a function that finds and returns a value by key in the map, or returns a defaultValue if not found.

import { Map } from "immutable";
import { curry } from "ramda";

export const nullishGet = curry(
  <V>(defaultValue: V, key: string, map: Map<string, V>): V =>
    map.get(key) ?? defaultValue
);

And I added a function to set the defaultValue to [] when the value I'm looking for is an array.

export const nullishGetArray = nullishGet<unknown[]>([]);

the type I expected:

Curry<(key: string, map: Map<string, unknown[]>) => unknown[]>

But it was

Curry<(defaultValue: unknown, key: string, map: Map<string, unknown>) => unknown>

I also wrote a version that doesn't specify the generic type. like this

export const nullishGetArray = nullishGet([])
//the type of nullishGetArray is Curry<(key: string, map: Map<string, unknown>) => unknown>

Simply put, it seems that the generic is not properly applied when trying to use it together with curry.

Is this because Ramda is not friendly with TypeScript, or am I doing something wrong?

Edit objective-zeh-6ye5og

If I were to write what I actually want without using curry, it would look like the following.

export const nullishGet = <V>(
  key: string | number,
  map: Map<string | number, V>,
  defaultValue: V
): V => {
  return map.get(key) ?? defaultValue
}

export const nullishGetArray = <V>(
  key: string | number,
  map: Map<string | number, V[]>,
  defaultValue: V[] = []
): V[] => nullishGet<V[]>(key, map, defaultValue)
library version

Solution

  • It's a bit awkward, but you can achieve what you're trying to do by moving the around a few things:

    import Immutable from "immutable";
    import * as R from "ramda";
    
    // Define implementation outside of Ramda's curry
    function nullishGetBase<V>(
      defaultValue: V,
      key: string,
      map: Immutable.Map<string, V>
    ): V {
      return map.get(key) ?? defaultValue;
    }
    
    // Note the explicit type here
    const nullishGetArray = R.curry(nullishGetBase<unknown[]>)([]);
    
    // result type is unknown[]
    const result = nullishGetArray("foo", Immutable.Map({ foo: ["bar"] }));
    

    I think it's failing with your version because the generic parameter V does not have any constraints, and it will default/fallback to unknown. In turn, that will override any other type you throw at it.