typescript 如何从有区别的联合类型中选择键?

qkf9rpyu  于 2023-01-14  发布在  TypeScript
关注(0)|答案(3)|浏览(154)

我有一个歧视联盟

type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };

我想直接从MyDUnion类型的区别联合体中访问name键。

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

这是无效的,因为这样我就必须导出MyNameMyDUnion类型以便在其他地方使用。
有什么想法吗?

ioekq8ef

ioekq8ef1#

为了过滤对象的并集,通常需要使用提取:简单的方法:

type Result = Extract<MyDUnion , {type: "anon"}>

更稳健:

type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };

type Values<T> = T[keyof T]

type Filter<Union, Type extends Partial<Union>> = Extract<Union, Type>

type Result = Filter<MyDUnion, { type: 'anon' }>
v8wbuo2f

v8wbuo2f2#

为了获得所需的密钥,您必须以某种方式告诉Typescript编译器
嘿编译器,我想了解一下这个对象,在一个非常特殊的情况下
在这个例子中,你想知道对象的信息,当type ==='anon'。那么,以你的例子为例,

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']

我知道这有点粗糙,但是你总是可以把它提取到泛型函数中,然后随意使用它们。我只是向你展示了基本的逻辑。你可以用它来使它更美观。

2hh7jdfx

2hh7jdfx3#

溶液

export type PickAll<
    Union,
    Keys extends KeyOf<Union>,
    Otherwise = undefined
> = {
    [_K in Keys]: Union extends { [K in _K]: infer Value }
        ? UnionToIntersection<Value>
        : Otherwise
}

助手

x一个一个一个一个x一个一个二个x
目标

type MyDUnion =
  | { type: 'anon'; name: string }
  | { type: 'google'; idToken: string }

indexed accesskeyof

MyDUnion['type']
/* "anon' | "google" */
// ❓

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?>

默认情况下,选择 * all *

type DUnionProps = PickAll<MyDUnion>
/* {
  type: "anon" | "google";
  name: string | undefined;
  idToken: string | undefined;
} */
// ✅

关注特定的Key(+IDE提示和类型检查)

ctrl + space处于工作状态

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)

相关问题