Reputation: 19839
I have a schema defined
type Schema = {
a: { a: 1 }
b: { b: 2 }
}
And I want a function to create objects that conform to multiple schemas.
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
I've tried a few other things. Interestingly when you &
a union with itself, it does a distributes the &
across all items in the union and doesn't quite get me what I want. I want some to operate on {a: 1} | {b: 2}
to get {a: 1, b: 2}
. Any ideas?
Upvotes: 1
Views: 1386
Reputation: 327954
Assuming the types inside your Schema
properties are not themselves unions, you can convert the union type Schema[K]
to an intersection using conditional types, like this:
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
This might be enough for you. If you have some other use case (e.g., if Schema
has a property like cd: {c: 3} | {d: 4}
and you want there to still be a union in the final type) a different solution could be more appropriate:
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>
) { }
That's similar except it walks through the keys of Schema
and then performs an intersection, instead of spreading the union of Schema[K]
. Again, the difference only shows up in cases where some of your schema properties may themselves be unions.
Okay, hope that helps. Good luck!
Upvotes: 9