Search code examples
typescripttype-safety

how to use Typescript values in subobject?


I am still very new to programming and typescript. I hope I can explain my question reasonably well, unfortunately I could not find a solution online.

I have a value variable like this for emails:

  contactId: string | undefined;
  from: string;
  to: string;
  cc: string;
  send?: boolean;

I have a sendMail function that I use very often and want to keep instead of making two new functions. This sendMail function gets a mail object passed to it and then uses the values to send a mail. The problem now is that sometimes the values are in mail and sometimes in mail.mutateMail. If I need for example the ID of the mail, it can be that I get it from mail.id or mail.mutateMail.id. But it is always either or, not both. This also works I cannot make the function typesafe like this.

Actually I need something like this:

export const sendMail =
  (mail: ValuesType, mail.mutateMail: ValuesType): AppThunk =>
{
 const mailId = !mail.mutateMail ? mail.id : mail.mutateMail.id;
 console.log(mailId)
}

But of course this does not work.

Can someone please tell me what is the best thing to do in this case to make the function typesafe?


Solution

  • But it is always either or, not both.

    You are describing a union.

    Your value can be one of two things. So you have two members in the union.

    type ValuesOrNested =
        (ValuesType & { mutateMail?: never }) | { mutateMail: ValuesType }
    

    That says the value can be one of two types.

    The first member type is your ValuesType object that does not have a mutateMail property. The second member type can be an object with a mutateMail property that that is ValuesType.

    Now you can use that type for your argument:

    export const sendMail =
      (mail: ValuesOrNested): AppThunk =>
    {
     const mailId = !mail.mutateMail ? mail.id : mail.mutateMail.id;
     console.log(mailId)
    }
    

    And the function should allow either format, but not both:

    // These work
    sendMail({ id: 1, contactId: 'qwe', from: 'a', to: 'b', cc: 'd' })
    sendMail({ mutateMail:  { id: 1, contactId: 'qwe', from: 'a', to: 'b', cc: 'd' } })
    
    // error as expected
    sendMail({
        id: 1, contactId: 'qwe', from: 'a', to: 'b', cc: 'd',
        mutateMail: { id: 1, contactId: 'qwe', from: 'a', to: 'b', cc: 'd' }
    })
    

    Playground