DEV Community

Tobiasz Ciesielski
Tobiasz Ciesielski

Posted on

Domain modeling with TypeScript - types with XOR

Consider a situation where you have an email object that can be send from User to Recipient. Both are a different interfaces. An implementation looks like this:

interface Email {
  id: number
  message: string
  from: Sender
  to: Recipient
}
Enter fullscreen mode Exit fullscreen mode

But when Recipient will answer to User we will have situation where we had to swap from with to .

Let’s adjust our interface to match these requirement. Field from and to will have both interfaces available.

interface Email {
  id: number
  message: string
  from: Recipient | Sender
  to: Recipient | Sender
}
Enter fullscreen mode Exit fullscreen mode

But now it is possible that from and to will have the same types.

const myEmail: Email = {
  id: 0,
  message: 'Hello',
  from: {} as Recipient,
  to: {} as Recipient,
}
Enter fullscreen mode Exit fullscreen mode

This situation is impossible in real world.

We should model our type to match only possible cases.

This is domain modeling.

In this case we can use XOR logic to make our type match exact domain requirement.

Here is XOR logic in table:

Input A Input B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0

As you can see only different values pass check and return positive output.

Here is the implementation.

type Email = {
  id: number
  message: string
} & (
  | {
      from: Recipient
      to: Sender
    }
  | {
      from: Sender
      to: Recipient
    }
)
Enter fullscreen mode Exit fullscreen mode

It looks like overengineering but it does the job perfectly. When we want to create interface which are both the same, we get an error.

const mailing: Email = {
  // Type '{ id: number; message: string; from: Recipient; to: Recipient; }' is not assignable to type 'Email'.
  id: 1,
  message: 'Hello',
  from: {} as Recipient,
  to: {} as Recipient,
}
Enter fullscreen mode Exit fullscreen mode

I hope this short post will be useful for you.

Jump to Typescript playground HERE to play with it :)

Here is also good example how to exclude some fields from your interface
https://stackoverflow.com/questions/44425344/typescript-interface-with-xor-barstring-xor-cannumber

See you soon! Have a great day :)

Top comments (0)