[英]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`
我還可以使用pipe
或compose
成功組合函數:
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;
一切對我來說都是正確的。 我想知道您是否需要將addManager
和increaseBudget
的結果聲明為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 種返回類型 ( R1
、 R2
、 R3
):
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.