Is it possible to define interface
or a type
in TS which will make sure that if prop A
is defined prop B
must be also (and if A
is not defined/set, B
mustn't be either). For example I have smth. like:
export interface IExample {
a: string;
b: () => void;
c: boolean;
}
Then if I have some object which implements IExample
I would like for TS to allow me either having just prop c
or both a
, b
and c
and throw compilation error if I have just a
and c
or b
and c
.
Better example is using this in React component like in this SO question or even better I want to achieve same functionality like in: this ReactPropType library.
I have tried to do this via discriminating unions, but I cannot get it to work and it gets too complex when I have couple of props like these (ie requriedIf
), but maybe I am just doing it all wrong. Here is example of my code:
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);
And here is how I am using it:
<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"}
/>
But TS is yelling at me saying that:
types of property 'firstTimeUserMessageTranslationKey' are incompatible.
Type 'string' is not assignable to type 'undefined'
And the whole idea behind my code was to make sure that if developer using NetworkRequestStatusMessage
must specify either all props or for example all of these props (or none of them): errorMessageTranslationKey
, errorCallOnActionTranslationKey
, networkErrorMessageTranslationKey
, onPressError
and similarly for INetworkRequestStatusMessageFisrtTimeUserProps