typescript - Kontrollera med TS att båda valfria parametrarna antingen anges eller är undefined


Jag skriver en app med React Native och Typescript och jag har en fråga.

Låt oss föreställa oss att vi har en enkel komponent med tre egenskaper, en obligatorisk och två valfria:

interface Props {
  requiredProp: string;
  optionalPropA: boolean;
  optionalPropB: Function;
}

I min app-logik måste antingen båda valfria egenskaper passeras eller ingen av dem och jag undrar hur jag kan kontrollera det med TS?

Jag kan bara komma på två sätt som jag kan uppnå det på:

  1. Utan att använda TS, kontrollera helt enkelt mitt villkor med if någonstans före return
  2. Kombinera två valfria egenskaper till en. Skapa ett objekt med två nycklar (en för varje egenskap) och skapa en TS-typ som säkerställer att antingen båda eller ingen av dem finns

Några exempel:

// Should be good because all params were passed
<SimpleComponent
  requiredProp={'Hello world'}
  optionalPropA={true}
  optionalPropB={() => {}}
/>
// Should be good because only required param was passed
<SimpleComponent
  requiredProp={'Hello world'}
/>
// Should complain that optionalPropB wasn't passed
<SimpleComponent
  requiredProp={'Hello world'}
  optionalPropA={true}
/>
// Should complain that optionalPropA wasn't passed
<SimpleComponent
  requiredProp={'Hello world'}
  optionalPropB={() => {}}
/>

Svar #1

Du behöver överbelasta din funktion:

Här :

TypeScript och React: Hur man överbelastar en högre ordningskomponent/dekoratör?

eller här: https://howtodoinjava.com/typescript/function-overloading/#:~:text=In%20TypeScript%2C%20function%20overloading%2C%20or,number%20of%20parameters%20or%20types.&text=Also%20note%20that%2C%20function%20implementation,with%20all%20the%20overloaded%20signatures.

Din funktion bör se ut ungefär så här:

interface PropsInterface {
   requiredProp: string
}

interface OptionalPropsInterface extends PropsInterface {
   optionalPropA: string,
   optionalPropB: string
}

function simpleComponent(data: PropsInterface): JSX.Element
function simpleComponent(data: OptionalPropsInterface): JSX.Element
function simpleComponent(): { 
   // ...
}

export {
   simpleComponent
}

Det kommer nu att kontrollera om en av de 2 överlagringarna matchar. Om inte kommer det att kasta ett fel/markera det med rött understrykning.

Svar #2

Du kan försöka använda diskriminerade unioner:

type Props = ({
  requiredProp: string
}) & (
  | { optionalPropA?: never, optionalPropB?: never }
  | { optionalPropA: boolean, optionalPropB?: never }
  | { optionalPropA?: never, optionalPropB: Function }
)

Faktiskt ville jag först föreslå en sådan lösning:

type Props = {
  requiredProp: string
} & (
  | {}
  | { optionalPropA: boolean }
  | { optionalPropB: Function }
)

Men av någon anledning hindrar det dig inte från att använda båda egenskaperna samtidigt, och det låter dig också skicka i princip vilka egenskaper som helst. Kanske är det för att {}-typen behandlas på något sätt annorlunda, är inte säker på detta, men den första koden borde fungera, även om den inte är lika snygg.