Home > Mobile >  Ramda filter and prop give errors when using typescript
Ramda filter and prop give errors when using typescript

Time:12-12

I am really new to Typescript and I am currently trying to convert some javascript functions with Ramda library usage into typescript functions, but as Ramda documentation does not have clear usage of typescript, this problem seems really hard for me to solve.

Basically, this is an arrow function "setParams", which accepts 2 arguments - params and newParams. Both are simple objects containing key and value, which is just a string.

Example:

const params = {
  select: '1, 2, 3',
  type: 'hi'
}

Function:

const setParams = (params: Record<string, string>, newParams: Record<string, string>): Record<string, string> => {
  const mergedParams: Record<string, string> = { ...params, ...newParams }

  return pipe(
    toPairs,
    filter(pipe(prop(1), isNil, not)),
    map(([key, value]) => ({ key, value })),
    sortWith([ascend(prop('key'))]),
    map(({ key, value }) => [key, value]),
    fromPairs
  )(mergedParams) as Record<string, string>
}

Hovering on filter in Webstorm gives this error message:

TS2345: Argument of type '<P extends never, C extends readonly P[] | Dictionary

>(collection: C) => C' is not assignable to parameter of type '(a: [string, unknown][]) => readonly never[]'.   Types of parameters 'collection' and 'a' are incompatible.     Type '[string, unknown][]' is not assignable to type 'readonly never[] | Dictionary'.       Type '[string, unknown][]' is not assignable to type 'Dictionary'.         Index signature for type 'string' is missing in type '[string, unknown][]'.

CodePudding user response:

Before adding the types, let's clean the function a bit, and make it pointfree while doing so. Start by merging the two objects, then pick all the non nil properties, convert to [key, value] pairs, sort the pairs, and convert the pairs back to an object:

const { pipe, mergeRight, toPairs, pickBy, complement, isNil, nth, sortBy, fromPairs } = R

const setParams = pipe(
  mergeRight, // merge objects to a new object
  pickBy(complement(isNil)), // take all non nil properties
  toPairs, // convert to key, value pairs
  sortBy(nth(0)), // sort the pairs
  fromPairs // convert back to an object
)

const params = {
  select: '1, 2, 3',
  type: 'hi'
}

const newParams = {
  type: 'hello',
  x: null
}

const result = setParams(params, newParams)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

If you haven't done so already install the @types/ramda package from NPM.

Now you can add the types explicitly to R.pipe. The 1st param (TArgs extends any[]) is a tuple (or array) of input arguments, and the rest (R1, R2, etc...) are the return type of each matching function inside the pipe. The last (R5 here) is also the return type of the entire pipe. Each combination of arguments has am overload deceleration. This is the deceleration for your case:

export function pipe<TArgs extends any[], R1, R2, R3, R4, R5>(
    f1: (...args: TArgs) => R1,
    f2: (a: R1) => R2,
    f3: (a: R2) => R3,
    f4: (a: R3) => R4,
    f5: (a: R4) => R5
): (...args: TArgs) => R5;

Types example (working sandbox). Deps - "@types/ramda": "^0.27.60", "ramda": "^0.27.1".

type Params = Record<string, string>;
type ParamsPairs = [string, string][];

const setParams = pipe<
  [Params, Params], // tuple of input arguments
  Params,
  Params,
  ParamsPairs,
  ParamsPairs,
  Params
>(
  mergeRight, // merge objects to a new object
  pickBy(complement(isNil)), // take all non nil properties
  toPairs, // convert to key, value pairs
  sortBy(nth(0)), // sort the pairs
  fromPairs // convert back to an object
);
  • Related