reactjs - Typescript kräver egenskapen om annan är definierad (requiredIf)
Är det möjligt att definiera interface eller en type i TS som säkerställer att om egenskapen A är definierad så måste egenskapen B också vara det (och om A inte är definierad/satt, får inte heller B vara det). Till exempel har jag något som:
export interface IExample {
a: string;
b: () => void;
c: boolean;
}
Då, om jag har ett objekt som implementerar IExample, skulle jag vilja att TS tillåter att jag antingen har bara egenskapen c eller både a, b och c och ger ett kompileringsfel om jag bara har a och c eller b och c.
Ett bättre exempel är att använda detta i en React-komponent som i den här SO-frågan eller ännu bättre vill jag uppnå samma funktionalitet som i: det här ReactPropType-biblioteket.
Jag har försökt göra detta via diskriminerande unioner, men jag kan inte få det att fungera och det blir för komplicerat när jag har ett par egenskaper som dessa (dvs requriedIf), men kanske gör jag bara allt fel. Här är ett exempel på min kod:
export interface INetworkRequestStatusMessageDefaultProps {
testID?: string;
networkRequestStatus: NetworkRequestStatus;
}
export interface INetworkRequestStatusMessageLoadingProps {
loadingMessageTranslationKey: string;
firstTimeUserMessageTranslationKey: string;
firstTimeUserCallOnActionTranslationKey: string;
onPressFirstTimeUser: () => void;
dataLoaded: boolean;
errorMessageTranslationKey: never;
errorCallOnActionTranslationKey: never;
networkErrorMessageTranslationKey: never;
onPressError: () => never;
deviceConnectedToNetwork: never;
}
export interface INetworkRequestStatusMessageFisrtTimeUserProps {
firstTimeUserMessageTranslationKey: string;
firstTimeUserCallOnActionTranslationKey: string;
onPressFirstTimeUser: () => void;
dataLoaded: boolean;
loadingMessageTranslationKey: never;
errorMessageTranslationKey: never;
errorCallOnActionTranslationKey: never;
networkErrorMessageTranslationKey: never;
onPressError: () => never;
deviceConnectedToNetwork: never;
}
export interface INetworkRequestStatusMessageErrorProps {
errorMessageTranslationKey: string;
errorCallOnActionTranslationKey: string;
networkErrorMessageTranslationKey: string;
onPressError: () => void;
deviceConnectedToNetwork: boolean;
firstTimeUserMessageTranslationKey: undefined;
firstTimeUserCallOnActionTranslationKey: never;
onPressFirstTimeUser: () => never;
dataLoaded: never;
loadingMessageTranslationKey: never;
}
export type NetworkRequestStatusMessageProps =
| (INetworkRequestStatusMessageDefaultProps &
(
| INetworkRequestStatusMessageLoadingProps
| INetworkRequestStatusMessageFisrtTimeUserProps
| INetworkRequestStatusMessageErrorProps))
| (INetworkRequestStatusMessageDefaultProps &
INetworkRequestStatusMessageErrorProps &
INetworkRequestStatusMessageFisrtTimeUserProps &
INetworkRequestStatusMessageLoadingProps);
Och här är hur jag använder det:
<NetworkRequestStatusMessage
networkRequestStatus={this.props.networkRequestStatus}
deviceConnectedToNetwork={this.props.deviceConnectedToNetwork}
dataLoaded={this.props.savedWorkoutsLoaded}
errorCallOnActionTranslationKey={"savedWorkouts.retry"}
errorMessageTranslationKey={"savedWorkouts.error"}
firstTimeUserCallOnActionTranslationKey={"savedWorkouts.callOnActionGoToCW"}
firstTimeUserMessageTranslationKey={"savedWorkouts.firstTimeUser"}
loadingMessageTranslationKey={"savedWorkouts.loading"}
networkErrorMessageTranslationKey={"savedWorkouts.networkError"}
onPressError={this.props.load}
onPressFirstTimeUser={() => {
tron.log("Clicked on go to CW");
}}
testID={"SAVED_WORKOUTS"}
/>
Men TS skriker åt mig och säger att:
types of property 'firstTimeUserMessageTranslationKey' are incompatible.
Type 'string' is not assignable to type 'undefined'
Och hela idén bakom min kod var att se till att om en utvecklare använder NetworkRequestStatusMessage måste specificera antingen alla egenskaper eller till exempel alla dessa egenskaper (eller ingen av dem): errorMessageTranslationKey, errorCallOnActionTranslationKey, networkErrorMessageTranslationKey, onPressError och på samma sätt för INetworkRequestStatusMessageFisrtTimeUserProps
Svar #1
Du bör använda en union för det:
type Example = {
a: string;
b: () => void;
c: boolean;
} | {
c: boolean;
}
