簡體   English   中英

以類型安全的方式在 pipe 中使用 Ramda 的克隆

[英]Use Ramda's clone in pipe in a type-safe way

我想使用 Ramda 以類型安全的方式克隆和更新對象(受此習語啟發),但我無法使其以類型安全的方式工作。

更新嵌套的 object 以類型安全的方式工作得很好:

interface Person {
  name: string
  department: {
    name: string
    budget: number
    manager?: string
  }
}
const personX: Person = {
  name: 'Foo Bar',
  department: {
    name: 'x',
    budget: 2000,
  },
}

const addManager = (name: string): (input: Person) => Person => assocPath([
  'department',
  'manager',
], name)
const x = addManager('Michael Scott')(personX) // x is of type `Person`

我還可以使用pipecompose成功組合函數:

const addManager = (name: string): (input: Person) => Person => assocPath([
  'department',
  'manager',
], name)
const increaseBudget = (budget: number): (input: Person) => Person => assocPath([
  'department',
  'budget',
], budget)
const addManagerAndUpdateBudget = pipe(addManager('MichaelScott'), increaseBudget(10000))
const x = addManagerAndUpdateBudget(personX) // x is still of type Person

但是,一旦我使用clone它就會失敗:

const addManager = (name: string): (input: Person) => Person => assocPath([
  'department',
  'manager',
], name)
const increaseBudget = (budget: number): (input: Person) => Person => assocPath([
  'department',
  'budget',
], budget)
const addManagerAndUpdateBudget = pipe(clone, addManager('MichaelScott'), increaseBudget(10000))
const x = addManagerAndUpdateBudget(personX) // Person is not assignable to readonly unknown[]

這可能是類型的問題嗎? 還是我在這里遺漏了什么?

(免責聲明:我是 Ramda 的核心團隊之一。)


Ramda 團隊在 TypeScript 中沒有太多的專業知識。我添加了definitelytyped標簽,因為該項目維護了通常的 Ramda 類型。

不太了解 TypeScript 打字,我不明白為什么這不起作用,就像我閱讀clone定義時一樣:

export function clone<T>(value: T): T;
export function clone<T>(value: readonly T[]): T[];

以及相關的pipe定義

export function pipe<TArgs extends any[], R1, R2, R3>(
    f1: (...args: TArgs) => R1,
    f2: (a: R1) => R2,
    f3: (a: R2) => R3,
): (...args: TArgs) => R3;

一切對我來說都是正確的。 我想知道您是否需要將addManagerincreaseBudget的結果聲明為Person 但這只是一個猜測,來自非 TS 人。


我回答主要是因為我想指出,對於許多用途,您不需要使用clone ,因為assocPath已經對它正在更改的任何數據執行了等效操作。

const person2 = increaseBudget  (10000) (person1)
person2 == person1 //=> false
person2 .department == person1 .department //=> false

當然assocPath在可以和不進行完整克隆的地方使用結構共享:

const person2 = assocPath ('startDate', '2014-07-12') (person1)
person2 .department == person1 .department //=> true

但是對於許多用途,特別是如果使用 Ramda 或其他不可變技術進行進一步修改時, clone根本就沒有必要。

——斯科特

當將R.pipe (或R.compose )與其他 Ramda 通用函數(例如R.clone )一起使用時,TS 有時無法推斷出正確的類型,以及創建的 function 的實際簽名。

注意:我使用的是 Ramda - 0.28.0 和 @types/ramda - 0.28.8。

在您的情況下,我們希望 Ramda 使用此簽名 - arguments 的列表傳遞給創建的 function ( TArgs ),然后是管道函數的 3 種返回類型 ( R1R2R3 ):

export function pipe<TArgs extends any[], R1, R2, R3>(
    f1: (...args: TArgs) => R1,
    f2: (a: R1) => R2,
    f3: (a: R2) => R3,
): (...args: TArgs) => R3;

由於 Ramda 不推斷它們,我們需要顯式添加它們( 沙箱):

const addManagerAndUpdateBudget = pipe<[Person], Person, Person, Person>(
  clone,
  addManager('MichaelScott'),
  increaseBudget(10000)
);

Arguments - 一個只有一個Person的元組,每個返回值也是一個Person 我們需要 state 所有這些,這樣 TS 就會使用我們需要的特定簽名。

另一種選擇是在 pipe 中顯式鍵入第一個 function,因此 TS 可以使用它來推斷其他類型( 沙箱):

const addManagerAndUpdateBudget = pipe(
  clone as (person: Person) => Person,
  addManager('MichaelScott'),
  increaseBudget(10000)
);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM