Ett Typescript-gränssnitt som tillåter en av två valfria nycklar eller ingen, men inte båda


Finns det ett sätt att definiera ett Typescript interface som tillåter ett av 2 valfria nycklar i ett objekt eller ingen av dem, men inte båda?

Här är ett förenklat exempel på vad jag försöker uppnå:

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. lösningen bör vara ett interface och inte ett type eftersom det i mitt användningsfall extends ett annat interface

Svar #1

type är isomorfisk med interface och du kan kombinera dessa två konstruktioner. Nedan är implementering med typunion och snitt, även lagt till exempel på interface för att visa att det fungerar bra, & är ekvivalent med extends. Tänk på:

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

Lösningen är långrandig, men passar ditt behov. Några förklaringar:

  • `bar?: never` används för att blockera möjligheten att ha ett värde som har ett sådant fält
  • & OtherInterface har exakt samma resultat som extends OtherInterface
  • {[K in any]: never} - representerar ett tomt objekt {}

Lekplatslänk

Relaterade artiklar