UPDATE: this answer was originally written before conditional types were introduced into the language. For newer versions of TypeScript, you can indeed transform arbitrary unions into intersections:
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
declare function retrieveValues<K extends keyof Properties>(
add?: K[]): UnionToIntersection<Properties[K]>;
const x = retrieveValues(['foo', 'bar']);
/* const x: {
n: number;
} & {
s: string;
} */
Or you could write something specifically to merge the properties that are picked from a type:
type PickMerge<T, K extends keyof T> =
{ [P in K]: { [Q in keyof T[P]]: [Q, T[P][Q]] }[keyof T[P]] }[K] extends infer U ?
[U] extends [[PropertyKey, any]] ? { [KV in U as KV[0]]: KV[1] } : never : never
declare function retrieveValues<K extends keyof Properties>(
add?: K[]): PickMerge<Properties, K>;
const x = retrieveValues(['foo', 'bar']);
/* const x: {
n: number;
s: string;
} */
More explanation upon request.
Playground link to code
TS 2.7- answer:
There's no straightforward type operator which, say, turns a union into an intersection, or allows you to iterate union types and do stuff programmatically with the pieces. So on the face of it you're stuck.
Backing up, if you allow yourself to build Properties
from pieces instead of trying to break the pieces apart, you can do this:
type InnerProperties = {
n: number;
s: string;
b: boolean;
}
type OuterProperties = {
foo: "n";
bar: "s";
baz: "b";
}
You can see how each key in OuterProperties
is a mapping to a key in InnerProperties
. (Note that in your Properties
, each outer property had a single inner property. You aren't restricted to that, though. If you wanted, say, the "foo"
outer key to correspond to something with multiple inner properties like {n: number, r: RegExp}
then you would add r: RegExp
to InnerProperties
and put foo: "n"|"r"
in OuterProperties
.)
Now you can pick out partial properties like this:
type PickProps<P extends keyof OuterProperties = keyof OuterProperties> = {
[K in OuterProperties[P]]: InnerProperties[K];
}
So PickProps<"foo">
is {n: number}
, and PickProps<"bar">
is {s: string}
, and PickProps<"baz">
is {b: boolean}
. And notice that PickProps<"foo"|"bar">
is {n: number; s: string}
, so we have the output type of retrieveValues()
ready. We still have to define Properties
in terms of InnerProperties
and OuterProperties
, like this:
type Properties = {
[K in keyof OuterProperties]: PickProps<K>
}
And finally you can declare that function the way you want it:
declare function retrieveValues<K extends keyof Properties>(add?: K[]): PickProps<K>;
const y: { n: number } & { s: string } = retrieveValues(['foo', 'bar']);
So that works. Hope that's helpful. Good luck!