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

Posted

技术标签:

【中文标题】打字稿:如何键入 Ramda R.prop(key) T\' 不可分配给类型为 \'(s: ) => 的参数【英文标题】:Typescript: How to type Ramda R.prop(key) T' is not assignable to parameter of type '(s: ) => 打字稿:如何键入 Ramda R.prop(key) T' 不可分配给类型为 '(s: ) => 的参数 【发布时间】:2019-08-03 04:09:21 【问题描述】:

打字稿错误

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

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

'(obj: Record) => T' 类型的参数不能分配给'(s: ) => ' 类型的参数。 参数“obj”和“s”的类型不兼容。 类型“”不可分配给类型“记录”。 ''.ts(2345) 类型中缺少索引签名

代码:

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));

如何使用这个函数

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

在我的实际应用中,这就是我的 IAsset 对象的签名:

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));  
  

但是它仍然会产生这个 Typescript 警告:

'(obj: Record) => T' 类型的参数不能分配给'(s: ) => ' 类型的参数。 参数“obj”和“s”的类型不兼容。 类型“”不可分配给类型“记录”。 ''.ts(2345) 类型中缺少索引签名

另外说明,Typescript 正在扼杀漂亮的小型 1 行 Ramda 函数的乐趣。

【问题讨论】:

从什么地方输入'(s: ) => @PrzemyslawPietrzak 我相信因为 Typescript 知道我正在更改对象的记录或键,并且 R.prop 适用于对象,并且 updateKey 函数接受对象以及要更改的新值。 Argument of type '(obj: Record) => T' is not assignable to parameter of type '(s: ) => '. 关于“Typescript 正在扼杀漂亮的小型 1 行 Ramda 函数的乐趣”,我在过去两年中听到了很多这样的消息。作为 Ramda 开发人员,我还没有发现它是核心问题。我们正在编写一个 JS 库,并希望尽可能让 JS 开发人员感到愉快。如果它与 Typescript 配合得很好,我们会很高兴,但我们不会为此牺牲我们的 JS API。到目前为止,似乎 TS 并不真正支持我们使用的所有构造。我很想看到这种变化,但我没有屏住呼吸。 【参考方案1】:

对于你原来的问题:

我不是 Ramda 方面的专家,所以也许我在使用这些类型时遗漏了一些东西,但看起来你可以通过使 updateKey 泛型来摆脱类型错误:

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

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


对于您更新的问题:

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

如果你看@types/ramdapropassoc 的单参数重载只接受字符串:

/**
 * 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;

我不确定,但我认为这是对 @types/ramda 项目的疏忽。您可以通过declaration merging 以这种方式增强类型定义:

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;

然后像这样输入您的updateKey 方法,无需单独的块来处理不同的密钥类型:

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

【讨论】:

再想一想,这可能不是毕竟是疏忽。在this example 中,ramda 似乎接受数字键,但数组被转换为对象,它们的键被转换为字符串。这使我得出结论,这些方法实际上并不适用于数组或数字字符串,您可能应该使用我建议的第一个选项。 谢谢!哦,是的,在我的 IAsset 中,密钥始终是字符串,我在示例中搞砸了。在我的界面中,实际上[key: string]: string | number | undefined | boolean; 表示键是字符串,但该键可以属于stringnumber undefined 或`boolean。

以上是关于打字稿:如何键入 Ramda R.prop(key) T' 不可分配给类型为 '(s: ) => 的参数的主要内容,如果未能解决你的问题,请参考以下文章

反应本机打字稿如何键入 FlatList

如何在打字稿中使用中间件键入 redux thunk

打字稿如何在对象中键入自定义键

如何在打字稿中键入枚举变量?

在打字稿中键入注释[重复]

在打字稿中以自引用方式键入对象