最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to select a key from a discriminated union type? - Stack Overflow

programmeradmin4浏览0评论

I have a disciminated union

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

I want to access the name key from the discriminative union, from the MyDUnion type directly. Something like this

type Name = MyDUnion['name']

But typescript won't allow that

Property 'name' doesn't exist on type '{ type: "anon"; name: string } | { type: "google"; idToken: string }'

How can I access it?

To be clear, this is not a valid solution:

type MyName = string;
type MyDUnion = { type: "anon"; name: MyName } | { type: "google"; idToken: string };
type Name = MyName; // this line would be in a different file

This is not valid, because then I would have to export both MyName and MyDUnion types to be used elsewhere.

Any ideas?

I have a disciminated union

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

I want to access the name key from the discriminative union, from the MyDUnion type directly. Something like this

type Name = MyDUnion['name']

But typescript won't allow that

Property 'name' doesn't exist on type '{ type: "anon"; name: string } | { type: "google"; idToken: string }'

How can I access it?

To be clear, this is not a valid solution:

type MyName = string;
type MyDUnion = { type: "anon"; name: MyName } | { type: "google"; idToken: string };
type Name = MyName; // this line would be in a different file

This is not valid, because then I would have to export both MyName and MyDUnion types to be used elsewhere.

Any ideas?

Share Improve this question asked Jan 13, 2023 at 10:32 sayandcodesayandcode 3,2642 gold badges18 silver badges32 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 7

In order to filter union of objects, usually you need to use Extract: The easy way:

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

The more robust:

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

Generic Solution

/**
 * @param Union - A discriminated union type to extract properties from.
 * @param Keys - The specific properties to extract from `Union`.
 * @defaultValue all `KeyOf<Union>`
 * @param Otherwise - The type to unionize with value types that don't exist in all members of `Union`.
 * @defaultValue `undefined`
 */
export type PickAll<
    Union,
    Keys extends KeyOf<Union> = KeyOf<Union>,
    Otherwise = undefined
> = {
    [_K in Keys]: Union extends { [K in _K]?: infer Value }
        ? UnionToIntersection<Value>
        : Otherwise
}

helpers

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

Target

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

indexed access and 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?>

by default, picks all

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

focus on a specific Key (+IDE prompts and type checking)

ctrl+space is OP

type DUnionName = PickAll<MyDUnion, 'name'>
/* { 
  name: string | undefined
} */
// ✅

or a union of Keys

type DesiredProps = PickAll<
    MyDUnion | { fish: number },
    'type' | 'idToken' | 'fish'
>
/* {
    type: "anon" | "google" | undefined;
    idToken: string | undefined;
    fish: number | undefined;
} // ✅ */

Gotchas

  • Does not distinguish between undefined and optional properties. It can be done though and its on the todo.

  • Extract literals directly.

DON'T do this:

type should be "anon" | "google"

type GoogleLike = PickAll<MyDUnion, 'type' | 'name'>

type g = GoogleLike['name' | 'type']
/* string | undefined 
发布评论

评论列表(0)

  1. 暂无评论