Typescript Interface - Possible to make "one or the other" properties required?

If you're truly after "one property or the other" and not both you can use never in the extending type:

interface MessageBasics {
  timestamp?: number;
  /* more general properties here */
}
interface MessageWithText extends MessageBasics {
  text: string;
  attachment?: never;
}
interface MessageWithAttachment extends MessageBasics {
  text?: never;
  attachment: string;
}
type Message = MessageWithText | MessageWithAttachment;

// 👍 OK 
let foo: Message = {attachment: 'a'}

// 👍 OK
let bar: Message = {text: 'b'}

// ❌ ERROR: Type '{ attachment: string; text: string; }' is not assignable to type 'Message'.
let baz: Message = {attachment: 'a', text: 'b'}

Example in Playground


You can use a union type to do this:

interface MessageBasics {
  timestamp?: number;
  /* more general properties here */
}
interface MessageWithText extends MessageBasics {
  text: string;
}
interface MessageWithAttachment extends MessageBasics {
  attachment: Attachment;
}
type Message = MessageWithText | MessageWithAttachment;

If you want to allow both text and attachment, you would write

type Message = MessageWithText | MessageWithAttachment | (MessageWithText & MessageWithAttachment);

Tags:

Typescript