ramda.js - Erori de tip Typescript pe Transducători Ramda și cum să faci față erorilor de tip confuze pe cod bun


Privind documentația Ramda pentru transduce, sunt prezentate două exemple, fiecare dintre ele determinând compilatorul TypeScript să arunce o eroare diferită.

Exemplul 1:

test('ex. 1', () => {
  const numbers = [1, 2, 3, 4]

  const transducer = compose(
    map(add(1)),
    take(2)
  )

  const result = transduce(transducer, flip(append), [], numbers)

  expect(result).toEqual([2, 3])
})

Typescript aruncă următoarea excepție pentru flip(append):

Argument of type '(arg1: never[], arg0?: {} | undefined) => <T>(list: readonly T[]) => T[]' is not assignable to parameter of type '(acc: ({} | undefined)[], val: {} | undefined) => readonly ({} | undefined)[]'.
      Types of parameters 'arg1' and 'acc' are incompatible.
        Type '({} | undefined)[]' is not assignable to type 'never[]'.
          Type '{} | undefined' is not assignable to type 'never'.
            Type 'undefined' is not assignable to type 'never'.

Dacă schimb flip(append) în flip(append) as any, codul funcționează așa cum mă aștept.

Exemplul 2:

test('ex. 2', () => {
  const isOdd = x => x % 2 === 1
  const firstOddTransducer = compose(
    filter(isOdd),
    take(1)
  )

  const result = transduce(
    firstOddTransducer,
    flip(append) as any,
    [],
    range(0, 100)
  )

  expect(result).toEqual([1])
})

Typescript aruncă următoarea excepție pentru firstOddTransducer:

Argument of type '(x0: readonly any[]) => Dictionary<any>' is not assignable to parameter of type '(arg: any[]) => readonly any[]'.
      Type 'Dictionary<any>' is missing the following properties from type 'readonly any[]': length, concat, join, slice, and 16 more.

La fel ca mai sus, dacă schimb firstOddTransducer în firstOddTransducer as any, codul funcționează conform așteptărilor.

Mai întâi, ce înseamnă de fapt aceste erori particulare?

În al doilea rând, care este cea mai bună modalitate de a aborda acest tip de probleme cu TypeScript funcțional? De foarte multe ori, când mă uit la diverse resurse de învățare pentru TypeScript, utilizatorii sunt avertizați să nu folosească any sau să nu folosească // @ts-ignore ca și cum ar fi ceva ce nu ar trebui făcut niciodată, dar cu cât codul meu devine mai complex și stilul meu de programare devine mai funcțional, cu atât primesc mai multe dintre aceste mesaje de eroare aparent incomprehensibile pentru cod perfect acceptabil. Nu mă deranjează să petrec puțin timp pentru a îmbunătăți tipurile, dar nu vreau să pierd prea mult timp depanând problemele legate de tipuri când știu că codul este bun.

În al treilea rând, există vreun sfat care să ajute în situațiile în care nu ești sigur dacă există o problemă cu tipurile sau cu TypeScript, ca mai sus, sau dacă există o problemă cu codul JavaScript, de exemplu, tehnici pentru a determina unde este problema reală astfel încât să poți fie investiga, fie ignora?

Răspuns #1

La momentul scrierii, aceasta este o limitare a TypeScript, pur și simplu nu există nicio modalitate prin care să exprimi că partea tipului trebuie să fie dedusă din utilizarea ulterioară.

Nu este decât atunci când apelezi transduce că de fapt legi toți parametrii de tip, deci, nu este un tip complet până atunci sau, mai bine spus, TypeScript nu știe cum să completeze tipul până nu faci asta. TypeScript va încerca să deducă tipul din context, așa că poate, dacă pui totul pe un singur rând, s-ar putea să reușească să facă asta (nu spun că va reuși).

Fundamental, un traductor este o abstractizare a unei secvențe/flux/observabil și oferi compilatorului aceste informații atunci când rulezi efectiv traductorul peste niște date. Atunci este nevoie să lege informația de tip a datelor.

Singurul lucru la care mă pot gândi aici este să folosești unknown ca tip de marcaj pentru date până execuți transduce sau into. Nu îți va oferi siguranță de tip, dar va suprima avertismentele compilatorului care, oricum, sunt fals pozitive. Nu este perfect, dar ar trebui să funcționeze.