Rafal Lesniak
Rafal Lesniak

Reputation: 556

Merge or operator in object Typescript

How to get rid of or operator (|) and merge all object properties to be all accessible?

Because now, typing payload.signInMethod TS complaints that this method does not exist in none "or group" but this one. How to write type to merge all object keys?

remove ors

Desired object: enter image description here

Playground to reproduce my problem: https://www.typescriptlang.org/play/#code/C4TwDgpgBAwg9gO2BAHsKBeKBvAUFKAUQDcIkBBMMAGwEsBjAQ2FsQC4d8CpGq6mWiAAoALRgGcIHccABOtBAHMA3FwC+qggGVaihAEkEAJQgAzDnm5RxugwgCyEYCLgATDgHJFcOIuoQPKAAfKA9TRnoIACMfAGtAkI8QRhc4BNCAV0lZBEYAWwDNKA0uQhQwWQhxcRgRCHpYuAzgCy4CMEYQAqRHZzchSskkDhi4f0YEIoJxEVoqBUVyV1dB8QGqshaoUfHJtqhKyNpSIUZZYAQIWUI8xlpqcRGfXamoCrg8uHhXKWs5BaKalwQNw9EQMigiic8CQqHQWAAFB0QNQ4Ix3LBEMg0ABtWIQEBwUyY2FoAC6AEpMAA+TjtTqo9EAOhsekMvRcrmUUAA9DyoAAJOAAdygwDgkKcB1origRLFs3EUCusjgsmBQA

Upvotes: 0

Views: 279

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249536

The usual way to deal with union types is to use type guards to narrow the union down to one of the members. In this case the in type guard works best:

type Context = {
  EventApplication: {
    applicationPhase: string;
  };
  SignInRef: {
    signInMethod: 'google' | 'facebook' | 'yahoo' | 'username';
  };
  ExpressCheckout: {
    paymentMethodPresent: boolean;
    shippingAddressPresent: boolean;
    receivePartnerEmails: boolean;
    promoCode: string;
  }
}

const getContext = (payload: Context[keyof Context]) => {
  if('signInMethod' in payload) {
    payload.signInMethod; 
  } else if('applicationPhase' in payload) {
    payload.applicationPhase
  } else{
    payload.promoCode
  }
}

Play

Now if you really want to have access to all members directly on payload, some | undefined will by necessity have to creep into the property types (since a property might be undefined on a specific union member). We can adapt StrictUnion from here to get the types to work the way you want:

type UnionKeys<T> = T extends any ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, undefined>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>

type Context = {
  EventApplication: {
    applicationPhase: string;
  };
  SignInRef: {
    signInMethod: 'google' | 'facebook' | 'yahoo' | 'username';
  };
  ExpressCheckout: {
    paymentMethodPresent: boolean;
    shippingAddressPresent: boolean;
    receivePartnerEmails: boolean;
    promoCode: string;
  }
}

const getContext = (payload: StrictUnion<Context[keyof Context]>) => {
  payload.applicationPhase // string | undefined 
}

Play

Upvotes: 2

Related Questions