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

185 views Asked by At

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
  • ramda: 0.28.0
  • @types/ramda: 0.28.23
1

There are 1 answers

2
Simon Ingeson On BEST ANSWER

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.