打字稿:如何输入 Ramda R.prop(key) T' 不能分配给类型为 '(s: {}) => {} 的参数

Leo*_*ban 5 javascript typescript ramda.js

打字稿错误

(方法) R.Static.prop(p: string): (obj: Record) => T (+3 重载)

返回一个函数,该函数在提供对象时返回该对象的指示属性(如果存在)。

Argument of type '(obj: Record) => T' is not assignable to parameter of type '(s: {}) => {}'. Types of parameters 'obj' and 's' are incompatible. Type '{}' is not assignable to type 'Record'. Index signature is missing in type '{}'.ts(2345)

在此处输入图片说明

The code:

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));
Run Code Online (Sandbox Code Playgroud)

How this function is used

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}
Run Code Online (Sandbox Code Playgroud)

In my actual app this is what the signature of my IAsset objects look like:

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;
}
Run Code Online (Sandbox Code Playgroud)
// 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));  
  }
}
Run Code Online (Sandbox Code Playgroud)

However it still produces this Typescript warning:

Argument of type '(obj: Record) => T' is not assignable to parameter of type '(s: {}) => {}'. Types of parameters 'obj' and 's' are incompatible. Type '{}' is not assignable to type 'Record'. Index signature is missing in type '{}'.ts(2345)

Also side note, Typescript is killing the fun of beautiful small 1-line Ramda functional functions.

p.s*_*w.g 2

对于你原来的问题:

我不是 Ramda 方面的专家,所以也许我遗漏了这些类型的使用方式,但看起来您可以通过通用来消除类型错误updateKey:

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

请注意,这里的类型推断不会很好。如果将鼠标悬停在 上updateKey("value"),推断的类型将为<{}, "value">(key: "value") => Lens,因此在某些情况下您可能需要显式指定类型参数。


对于您更新的问题:

我肯定缺少一些东西。您的界面IAsset不支持数字键,仅支持字符串。因此,除了字符串之外,不需要担心任何其他事情,但是,为了论证,让我们假设您还想处理数字或符号键。

如果您查看@types/ramda,会发现 的单参数重载prop并且assoc仅接受字符串:

/**
 * 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;
Run Code Online (Sandbox Code Playgroud)

我不确定,但我相信这是对该@types/ramda项目的疏忽。您可以通过以下方式通过声明合并来增强类型定义:

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;
}
Run Code Online (Sandbox Code Playgroud)

然后updateKey像这样输入你的方法,不需要单独的块来处理不同的键类型:

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