ramda.js - Ramda filter och prop ger fel när man använder TypeScript


Jag är verkligen ny på Typescript och jag försöker för närvarande konvertera några javascriptfunktioner med användning av Ramda-biblioteket till typescriptfunktioner, men eftersom Ramda-dokumentationen inte har tydlig användning av typescript verkar detta problem vara väldigt svårt för mig att lösa.

I grund och botten är detta en pilfunktion "setParams", som tar emot 2 argument - params och newParams. Båda är enkla objekt som innehåller nyckel och värde, som bara är en sträng.

Exempel :

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

Funktion :

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>
}

Att hovra över filtret i Webstorm ger detta felmeddelande:

TS2345: Argumentet av typen '<P extends never, C extends readonly P[] | Dictionary<P>>(collection: C) => C' kan inte tilldelas parametern av typen '(a: [string, unknown][]) => readonly never[]'. Typerna för parametrarna 'collection' och 'a' är inkompatibla. Typen '[string, unknown][]' kan inte tilldelas typen 'readonly never[] | Dictionary'. Typen '[string, unknown][]' kan inte tilldelas typen 'Dictionary'. Indexsignaturen för typen 'string' saknas i typen '[string, unknown][]'.

Svar #1

Innan vi lägger till typerna, låt oss städa upp funktionen lite och göra den pointfree samtidigt. Börja med att slå ihop de två objekten, välj sedan alla icke-nil egenskaper, konvertera till [key, value]-par, sortera paren och konvertera paren tillbaka till ett objekt:

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>

Om du inte redan har gjort det, installera @types/ramda-paketet från NPM.

Nu kan du lägga till typerna explicit till R.pipe. Den första parametern (TArgs extends any[]) är en tuppel (eller array) av inmatningsargument, och resten (R1, R2, etc...) är returtypen för varje matchande funktion inuti pipen. Den sista (R5 här) är också returtypen för hela pipen. Varje kombination av argument har en överbelastningsdeklaration. Detta är deklarationen för ditt fall:

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;

Typer exempel (arbets-sandbox). Beroenden - "@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
);