在打字稿中键入对象文字的安全合并 [英] Type safe merge of object literals in typescript
问题描述
我想合并两个打字稿对象(使用对象传播):
var one = { a: 1 }
var two = { a: 2, b: 3 }
var m = {...one, ...two} // problem as property `a` is overwritten
我想使用类型系统来确保第二个对象中的任何属性都不会覆盖第一个对象中的任何属性.我不确定为什么以下解决方案不起作用:
type UniqueObject<T extends {[K in keyof U]?: any}, U> =
{[K in keyof U]: T[K] extends U[K] ? never : U[K]}
var one = { a: 1 }
var two1 = { a: 2, b: 3 }
var two1_: UniqueObject<typeof one, typeof two1> = two1 // errors correctly
var two2 = { a: undefined, b: 1 }
var two2_: UniqueObject<typeof one, typeof two2> = two2 // passes incorrectly
一年前的另一个版本我当时以为是undefined extends U[K]
代替T[K] extends U[K]
:
type UniqueObject<T extends {[K in keyof U]?: any}, U> =
{[K in keyof U]: undefined extends T[K] ? U[K]: never}
这两个都不起作用.我怀疑这是因为undefined extends U[K]
或T[K] extends U[K]
都是错误的,因为T
中的属性K
是可选的.不知道如何或是否有可能解决这个问题.
您的版本或多或少都等效-只有条件类型中的true/false分支会被切换.
约束T extends {[K in keyof U]?: any}
有点问题:在two
中删除a
时,会触发错误Type '{ a: number; }' has no properties in common with type '{ b?: any; }
,这实际上应该是成功的情况.
还请注意,生成的类型merge
不包含两种类型的合并类型定义.我们可以更改声明:
type UniqueObject<T, U> =
T & { [K in keyof U]: K extends keyof T ? never : U[K] }
现在,编译器使用重复的a
属性正确地错误:
var one = { a: 1 }
var two = { a: 2, b: 3 }
// v a becomes never here
type Merge = UniqueObject<typeof one, typeof two> // { a: never; b: number; }
const res: Merge = { ...one, ...two } // errors now, cannot assign number to never
在下文中,我对类型进行了一些简化,并将所有内容打包在紧凑的辅助函数中,以控制类型+传播算子:
function mergeUnique<T extends object, U extends object & { [K in keyof U]: K extends keyof T ? never : U[K] }>(o1: T, o2: U) {
return { ...o1, ...o2 }
}
const res21 = mergeUnique({ a: 1 }, { b: 3 })
const res22 = mergeUnique({ a: 1 }, { a: 2, b: 3 }) // error
const res23 = mergeUnique({ a: 1, c: 5 }, { b: 3 })
const res24 = mergeUnique({ a: 1}, { a: undefined }) // error
I want to merge two typescript objects (using object spread):
var one = { a: 1 }
var two = { a: 2, b: 3 }
var m = {...one, ...two} // problem as property `a` is overwritten
I want to use the type system to ensure none of the properties in the second object overwrite any properties in the first. I am not sure why the following solution does not work:
type UniqueObject<T extends {[K in keyof U]?: any}, U> =
{[K in keyof U]: T[K] extends U[K] ? never : U[K]}
var one = { a: 1 }
var two1 = { a: 2, b: 3 }
var two1_: UniqueObject<typeof one, typeof two1> = two1 // errors correctly
var two2 = { a: undefined, b: 1 }
var two2_: UniqueObject<typeof one, typeof two2> = two2 // passes incorrectly
Another version from a year ago which I thought worked at the time had undefined extends U[K]
in the place of T[K] extends U[K]
:
type UniqueObject<T extends {[K in keyof U]?: any}, U> =
{[K in keyof U]: undefined extends T[K] ? U[K]: never}
Neither of these two work. I suspect it is because the undefined extends U[K]
or T[K] extends U[K]
are both false as the property K
in T
is optional. Not sure how or if it's possible to get around this.
Both your versions are more or less equivalent - only the true/false branches in the conditional type are switched up.
The constraint T extends {[K in keyof U]?: any}
is a bit problematic: when you remove a
in two
, the error Type '{ a: number; }' has no properties in common with type '{ b?: any; }
is triggered, which actually should be the success case.
Be also aware, that the resulting type merge
doesn't contain the merged type definition from both types. We can change the declaration up:
type UniqueObject<T, U> =
T & { [K in keyof U]: K extends keyof T ? never : U[K] }
Now, the compiler correctly errors with a duplicate a
property:
var one = { a: 1 }
var two = { a: 2, b: 3 }
// v a becomes never here
type Merge = UniqueObject<typeof one, typeof two> // { a: never; b: number; }
const res: Merge = { ...one, ...two } // errors now, cannot assign number to never
In the following I have simplified the type a bit and packed everything in a compact helper function to control types + spread operator:
function mergeUnique<T extends object, U extends object & { [K in keyof U]: K extends keyof T ? never : U[K] }>(o1: T, o2: U) {
return { ...o1, ...o2 }
}
const res21 = mergeUnique({ a: 1 }, { b: 3 })
const res22 = mergeUnique({ a: 1 }, { a: 2, b: 3 }) // error
const res23 = mergeUnique({ a: 1, c: 5 }, { b: 3 })
const res24 = mergeUnique({ a: 1}, { a: undefined }) // error
这篇关于在打字稿中键入对象文字的安全合并的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!