如何合并联合类型中所有对象的属性?

时间:2019-05-20 00:49:33

标签: typescript

我定义了一个模式

>>> from bs4 import BeautifulSoup
>>> a = '<tr>\n<td>Country</td>\n<th>Australia</th>\n</tr>'
>>> soup = BeautifulSoup(a, 'html.parser')
>>> country = soup.find('th').text
>>> country
Australia

我想要一个函数来创建符合多个架构的对象。

type Schema = {
    a: { a: 1 }
    b: { b: 2 }
}

playground link

我尝试了其他一些事情。有趣的是,当您function createObject<K extends keyof Schema>(schema: Array<K>, obj: Schema[K]) {} createObject(["a"], { a: 1 }) // works createObject(["b"], { b: 2 }) // works createObject(["a", "b"], { b: 2 }) // doesn't error but it should createObject(["a", "b"], { a: 1, b: 2 }) // works 与自己的联合时,它会在整个联合中的所有项目之间分配&,并不能完全满足我的需求。我希望有人对&进行操作以获得{a: 1} | {b: 2}。有什么想法吗?

1 个答案:

答案 0 :(得分:5)

假设Schema属性中的类型本身不是并集,则可以convert the union type Schema[K] to an intersection using conditional types,如下所示:

type Schema = {
    a: { a: 1 }
    b: { b: 2 }
};    

type UnionToIntersection<U> =
    (U extends any ? (k: U) => void : never) extends
    ((k: infer I) => void) ? I : never

function createObject<K extends keyof Schema>(
    schema: Array<K>, 
    obj: UnionToIntersection<Schema[K]>
) { }

createObject(["a"], { a: 1 }) // works
createObject(["b"], { b: 2 }) // works
createObject(["a", "b"], { b: 2 }) // error! 
createObject(["a", "b"], { a: 1, b: 2 }) // works

这对您来说足够了。如果您还有其他用例(例如,如果Schema具有cd: {c: 3} | {d: 4}之类的属性,并且您希望最终类型中仍然存在并集),则更合适的解决方案可能是:

type PropsToIntersection<T, K extends keyof T> =
    { [P in K]: (k: T[P]) => void }[K] extends
    ((k: infer I) => void) ? I : never;

function createObject<K extends keyof Schema>(
    schema: Array<K>,
    obj: PropsToIntersection<Schema, K>
) { }

相似之处在于它遍历Schema的键然后执行交集,而不是扩展Schema[K]的并集。同样,仅在某些架构属性本身可能是联合的情况下才会显示出差异。

好的,希望能有所帮助。祝你好运!