javascript - Typescript: Hvordan typisere Ramda R.prop(key) T' kan ikke tildeles parameter av typen '(s: {}) => {}


Typescript-feil

(metode) R.Static.prop(p: string): (obj: Record) => T (+3 overbelastninger)

Returnerer en funksjon som, når den får et objekt, returnerer den angitte egenskapen til objektet, hvis den eksisterer.

Argument av typen '(obj: Record) => T' kan ikke tildeles parameter av typen '(s: {}) => {}'. Typer av parametere 'obj' og 's' er inkompatible. Typen '{}' kan ikke tildeles typen 'Record'. Indekssignatur mangler i typen '{}'.ts(2345)

Koden:

https://ramdajs.com/docs/#lens

// Changes value of key in object without mutation
export const updateKey = (key: string) => R.lens(R.prop(key), R.assoc(key));

Hvordan denne funksjonen brukes

interface IMyObj {
  position: number;
  price: number;
  value: number;
}

const myObj: IMyObj = {
  position: 1,
  price: 100,
  value: 100
}

const updateKey = (key: string) => R.lens(R.prop(key), R.assoc(key));

const newObj = R.set(updateKey('value'), 200, myObj);

console.log('newObj', newObj); // {"position":1,"price":100,"value":200}

I min faktiske app ser signaturen til mine IAsset-objekter slik ut:

export interface IAsset {
  [key: string]: string | number | undefined | boolean;
  availableSupply?: string;
  currency: string;
  exchange: string;
  exchange_base?: string;
  marketCap: number;
  name: string;
  percentage?: number;
  price: number;
  position?: number;
  value?: number;
  inWatchlist?: boolean;
}
// Changes value of key in object without mutation
export const updateKey = (key: IAsset[string]) => {
  if (key) {
    return R.lens(R.prop(key), R.assoc(key));  
  }
}

Likevel gir det fortsatt denne Typescript-advarselen:

Argument av typen '(obj: Record) => T' kan ikke tildeles parameter av typen '(s: {}) => {}'. Typer av parametere 'obj' og 's' er inkompatible. Type '{}' kan ikke tildeles til typen 'Record'. Indekssignatur mangler i typen '{}'.

Forresten, Typescript dreper gleden ved vakre små 1-linjers Ramda funksjonelle funksjoner.

Svar #1

For ditt opprinnelige spørsmål:

Jeg er ingen ekspert på Ramda, så kanskje jeg misser noe med hvordan disse typene brukes, men det ser ut som du kan bli kvitt typefeilene ved å gjøre updateKey generisk:

const updateKey = <T, K extends string>(key: K) => R.lens(R.prop<K, T>(key), R.assoc(key));

Merk at typeinferensen her ikke vil være flott. Hvis du holder musen over updateKey("value"), vil den infererte typen være <{}, "value">(key: "value") => Lens, så du må kanskje spesifikt angi typeargumenter i visse scenarier.

For ditt oppdaterte spørsmål:

Det er definitivt noe jeg går glipp av. Grensesnittet ditt for IAsset støtter ikke numeriske nøkler—kun strenger. Så det bør ikke være behov for å bekymre seg for annet enn strenger, men la oss anta for argumentets skyld at du også ønsker å håndtere numeriske eller symbolske nøkler.

Hvis du ser på @types/ramda, aksepterer de enkle parameter-overloadene av prop og assoc kun strenger:

/**
 * Returns a function that when supplied an object returns the indicated property of that object, if it exists.
 */
prop<T>(__: Placeholder, obj: T): <P extends keyof T>(p: P) => T[P];
prop<P extends keyof T, T>(p: P, obj: T): T[P];
prop<P extends string>(p: P): <T>(obj: Record<P, T>) => T;
prop<P extends string, T>(p: P): (obj: Record<P, T>) => T;

/**
 * Makes a shallow clone of an object, setting or overriding the specified property with the given value.
 */
assoc<T, U>(__: Placeholder, val: T, obj: U): <K extends string>(prop: K) => Record<K, T> & U;
assoc<U, K extends string>(prop: K, __: Placeholder, obj: U): <T>(val: T) => Record<K, T> & U;
assoc<T, U, K extends string>(prop: K, val: T, obj: U): Record<K, T> & U;
assoc<T, K extends string>(prop: K, val: T): <U>(obj: U) => Record<K, T> & U;
assoc<K extends string>(prop: K): <T, U>(val: T, obj: U) => Record<K, T> & U;

Jeg er ikke sikker, men jeg tror dette er en forglemmelse i @types/ramda-prosjektet. Du kan forbedre typedefinisjonen gjennom deklarasjonssammenslåing på denne måten:

interface Static {
    prop<P extends keyof T, T>(p: P): (obj: Record<P, T>) => T;
    assoc<K extends keyof T, T>(prop: K): <T, U>(val: T, obj: U) => Record<K, T> & U;
}

Og skriv deretter updateKey-metoden din slik, uten behov for separate blokker for å håndtere forskjellige nøkkeltyper:

const updateKey = <K extends keyof IAsset>(key: K) => R.lens(R.prop<K, IAsset>(key), R.assoc<K, IAsset>(key));

Relaterte artikler