更新:这个答案最初是在 conditional types 被引入语言之前编写的。对于较新版本的 TypeScript,您确实可以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;
} */
或者你可以专门写一些东西来合并从一个类型中选择的属性:
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;
} */
应要求提供更多解释。
Playground link to code
TS 2.7- 答案:
没有直接的类型运算符可以将联合转换为交集,或者允许您iterate union types 并以编程方式处理这些片段。所以从表面上看,你被卡住了。
备份,如果您允许自己从碎片构建Properties,而不是试图将碎片分开,您可以这样做:
type InnerProperties = {
n: number;
s: string;
b: boolean;
}
type OuterProperties = {
foo: "n";
bar: "s";
baz: "b";
}
您可以看到OuterProperties 中的每个键如何映射到InnerProperties 中的键。 (请注意,在您的Properties 中,每个外部属性都有一个内部属性。不过,您不限于此。如果您想要,例如,"foo" 外部键对应于具有多个内部属性的东西,例如 @ 987654333@ 然后您将r: RegExp 添加到InnerProperties 并将foo: "n"|"r" 放入OuterProperties。)
现在你可以像这样选择部分属性:
type PickProps<P extends keyof OuterProperties = keyof OuterProperties> = {
[K in OuterProperties[P]]: InnerProperties[K];
}
所以PickProps<"foo"> 是{n: number},PickProps<"bar"> 是{s: string},PickProps<"baz"> 是{b: boolean}。注意PickProps<"foo"|"bar"> 是{n: number; s: string},所以我们准备好了retrieveValues() 的输出类型。我们仍然需要根据InnerProperties 和OuterProperties 来定义Properties,如下所示:
type Properties = {
[K in keyof OuterProperties]: PickProps<K>
}
最后你可以按照你想要的方式声明这个函数:
declare function retrieveValues<K extends keyof Properties>(add?: K[]): PickProps<K>;
const y: { n: number } & { s: string } = retrieveValues(['foo', 'bar']);
这样就行了。希望这会有所帮助。祝你好运!