itaydafna
itaydafna

Reputation: 2086

A Typescript interface that allows one of 2 optional keys or none but not both

Is there a way to define a Typescript interface which allows one of 2 optional keys in an object or none of them, but not both?

Here is a simplified example of what I'm trying to achieve:

const example1 = { foo: 'some string' }; //should pass - can have only "foo: string"

const example2 = { bar: 42 }; // should pass - can have only "bar: number"

const example3 = {}; // should pass - both foo and bar are optional

const example4 = { foo: 'some string', bar: 42 }; // should throw a Typescript error - can't have both foo and bar simultaneously;

PS. solution should be an interface and not a type since in my use-case it extends another interface

Upvotes: 7

Views: 2687

Answers (1)

Maciej Sikora
Maciej Sikora

Reputation: 20162

type is isomorphic to interface and you can join these two constructs. Below implementation by type union and intersection, also added example interface to show it works well, & is equivalent to extends. Consider:

interface OtherInterface {
    field?: string
}
type Example = ({
    foo: string
    bar?: never
} | {
    foo?: never
    bar: number
} | {
    [K in any]: never
}) & OtherInterface

const example1: Example = { foo: 'some string' }; // ok

const example2: Example = { bar: 42 }; // ok

const example3: Example = {}; // ok

const example4: Example = { foo: 'some string', bar: 42 }; // error

The solution is verbose, but match your need. Some explanations:

  • bar?: never is used in order to block possibility to have a value which has such field
  • & OtherInterface has exactly the same result as extends OtherInterface
  • {[K in any]: never} - represents empty object {}

Playground link

Upvotes: 8

Related Questions