【问题标题】:How to write a generic object assign|merge function in typescript?如何在打字稿中编写通用对象分配|合并函数?
【发布时间】:2021-10-17 16:52:37
【问题描述】:

我如何将 {...a,...b} 的等价物编写为 typescript 4.4+ 中的通用函数。我知道ab 都是记录,但事先不知道它们是什么。我想创建一个泛型类型,将任意函数限制为 {...a,...b} 操作。

不起作用的示例:

type Rec = Record<string,unknown>
type ExtendRec = <X extends Rec, Y extends X>(x: X) => Y
// or <X extends Rec, Y extends Rec>(x: X) => Y&X
const addA:ExtendRec = <X extends Rec>(x:X) => ({...x, a: 'a'})
const addB:ExtendRec = <X extends Rec>(x:X) => ({...x, b: 'b'})
const addC:ExtendRec = <X extends Rec>(x:X) => ({...x, c: 'c'})
const blank = {} // expected {}
const a = addA(blank) // expected {a:string}, actual:{}
const ab = addB(a) // expected {a:string,b:string}, actual:{}
const abc = addC(ab) // expected {a:string,b:string,c:string}, actual:{}

addA、addB、addC 函数的类型错误是:

Type '<X extends Rec>(x: X) => X & { c: string; }' is not assignable to type 'ExtendRec'.
  Type 'X & { c: string; }' is not assignable to type 'Y'.
    'X & { c: string; }' is assignable to the constraint of type 'Y', but 'Y' could be instantiated with a different subtype of constraint 'Record<string, unknown>'.

令人费解的是,如果我只是删除 ExtendRec 作为函数注释,它就可以工作,因此 typescript 已经能够正确推断对象分配操作,但我不能编写一个通用函数类型来约束任意函数扩展操作。

【问题讨论】:

  • Rec 声明在哪里?
  • 您说ab 是记录,但它们在您的代码中被分配了字符串?
  • 添加了推荐。 a、ab 和 abc 反映了结果应该具有哪些属性。

标签: typescript


【解决方案1】:

我认为这种类型需要一个泛型(用于捕获将要添加的属性)以及函数(用于捕获正在扩展的对象)。

例如:

type ExtendRec<R extends Record<string, unknown>> = <X>(x: X) => X & R

现在其余的工作如你所愿:

type ExtendRec<R extends Record<string, unknown>> = <X>(x: X) => X & R

const addA: ExtendRec<{ a: string }> = (x) => ({ ...x, a: 'a'})
const addB: ExtendRec<{ b: string }> = (x) => ({ ...x, b: 'b'})
const addC: ExtendRec<{ c: string }> = (x) => ({ ...x, c: 'c'})

const blank = {} // expected {}
const a = addA(blank) // expected {a:string}
const ab = addB(a) // expected {a:string,b:string}
const abc = addC(ab) // expected {a:string,b:string,c:string}

Playground


或者也许这种方法使用带有泛型的合并函数作为两个参数。 addA 现在只使用一个参数是泛型而另一个是已知类型的参数。

type MergeFn = <A extends object, B extends object>(a: A, b: B) => A & B
const merge: MergeFn = (a, b) => ({ ...a, ...b })

const addA = <X extends object>(x: X) => merge(x, { a: 'a'})
const addB = <X extends object>(x: X) => merge(x, { b: 'b'})
const addC = <X extends object>(x: X) => merge(x, { c: 'c'})

Playground

这和前面的例子有点像。在每种情况下,在声明 addA 时,以某种方式静态声明添加的类型,并在调用 addA 时推断出 X

请注意,我使用 object 而不是 Record&lt;string, unknown&gt;,因为它更短,但这在这里并不是很重要。

【讨论】:

  • 第二个效果很好,因为它不需要知道确切的属性是什么。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
  • 1970-01-01
  • 1970-01-01
  • 2018-09-15
  • 2019-07-17
相关资源
最近更新 更多