如何编写{…a,…B}作为typescript 4.4+中的泛型函数。我知道a
和b
都是记录,但是事先不知道它们是什么。我想创建一个泛型类型,将任意函数约束为{…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已经有能力推断对象分配操作正确,但我不能写一个通用的函数类型,约束任意函数的扩展操作。
我认为这种类型需要一个泛型(捕获将要添加的属性),以及函数(捕获正在扩展的对象)。
例如:
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}
游乐场
或者这种方法对两个参数都使用带泛型的合并函数。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'})
游乐场
这和前面的例子是一样的。在每种情况下,添加的类型在声明addA
时以某种方式静态声明,而在调用addA
时推断X
。
注意,我使用object
而不是Record<string, unknown>
,因为它更短,但这在这里并不太重要。