[英]How to select a key from a discriminated union type?
我有一个受歧视的工会
type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };
我想直接从MyDUnion
类型的判别联合访问名称键。 像这样的东西
type Name = MyDUnion['name']
但是 typescript 不允许
Property 'name' doesn't exist on type '{ type: "anon"; name: string } | { type: "google"; idToken: string }'
我怎样才能访问它?
需要明确的是,这不是一个有效的解决方案:
type MyName = string;
type MyDUnion = { type: "anon"; name: MyName } | { type: "google"; idToken: string };
type Name = MyName; // this line would be in a different file
这是无效的,因为那样的话我将不得不导出MyName
和MyDUnion
类型以便在别处使用。
有任何想法吗?
为了过滤对象的联合,通常你需要使用Extract :最简单的方法:
type Result = Extract<MyDUnion , {type: "anon"}>
更健壮的:
type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };
type Filter<Union, Type extends Partial<Union>> = Extract<Union, Type>
type Result = Filter<MyDUnion, { type: 'anon' }>
export type PickAll<
Union,
Keys extends KeyOf<Union>,
Otherwise = undefined
> = {
[_K in Keys]: Union extends { [K in _K]: infer Value }
? UnionToIntersection<Value>
: Otherwise
}
type KeyOf<Union, Otherwise = never> = Union extends Union
? keyof Union
: Otherwise
type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never
) extends (k: infer I) => void
? I
: never
type MyDUnion =
| { type: 'anon'; name: string }
| { type: 'google'; idToken: string }
indexed access
和keyof
MyDUnion['type']
/* "anon' | "google" */
// OK
MyDUnion[keyof MyDUnion]
/* "anon" | "google" */
// ❓
// @ts-expect-error - not all union members have an idToken
MyDUnion['type' | 'idToken']
/* any */
// ❌
KeyOf<Union>
type UnionKeys = KeyOf<MyDUnion>
/* "type" | "name" | "idToken" */
// ✅
PickAll<Union, KeyOf<Union>?, Otherwise?>
默认情况下,选择所有
type DUnionProps = PickAll<MyDUnion>
/* {
type: "anon" | "google";
name: string | undefined;
idToken: string | undefined;
} */
// ✅
Key
(+IDE 提示和类型检查)
ctrl
+space
是OP
type DUnionName = PickAll<MyDUnion, 'name'>
/* {
name: string | undefined
} */
// ✅
Keys
的联合type DesiredProps = PickAll<
MyDUnion | { fish: number },
'type' | 'idToken' | 'fish'
>
/* {
type: "anon" | "google" | undefined;
idToken: string | undefined;
fish: number | undefined;
} // ✅ */
type
应该是"anon"
| "google"
type GoogleLike = PickAll<MyDUnion, 'type' | 'name'>
type g = GoogleLike['name' | 'type']
/* string | undefined 🥴 */
type GoogleLikeTypes = GoogleLike['type']
/* "anon" | "google" */
// ✅
type GoogleLikeNames = GoogleLike['name']
/* string | undefined */
// ✅ (weird, be mindful)
我忘了提到第三个参数可以用来改变回退类型。 默认是未定义的,我认为这是最类型安全的行为,但你也可以将其设置为任何你想要的。 例如, PickAll<Union, Keys, never>
等同于Required<PickAll<Union, Keys>
>,或者至少如果Required
可以推断出像PickAll
这样的类型,那么它会是等价的。
为了获得所需的密钥,您必须以某种方式告诉 Typescript 编译器
嘿编译器,我想知道这个 object,在一个非常具体的情况下
在这种情况下,当type ==='anon'
时,您想了解 object。 所以,以你的例子,
type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };
type SpclCase = MyDUnion & {type: "anon"}
通过这样做,您将获得有关这两种情况何时重叠的信息。 现在您可以直接索引name
键。
type Name = SpclCase['name']
如果你想让它更健壮,我们可以确保我们使用的缩小类型(在本例中为{type: 'anon'}
)满足所需的形状。 这是一个临时的解决方案。
type DiscriminatorObj = { type: 'anon' }
type RequiredCase = DiscriminatorObj extends Pick<MyDUnion, "type"> ? DiscriminatorObj: never;
type SpclCase = (RequestBody & RequiredCase);
type Name = SpclCase['name']
我知道它的边缘有点粗糙,但你总是可以将它提取到通用函数中并随意使用它们。 我只是向你展示了基本逻辑。 您可以使用它使它更美观。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.