I'm trying to create a component in ReactJS and manage prop types using typescript
function AccountInfoCard(props: AccountInfoCardProps) {
const { data, isLoading } = props;
if (isLoading) {
return <Text>Loading...</Text>;
}
// In this area isLoading must be false and data will be provided correctly.
// no need for optional chaining like this data?.balance
return <Text>{data.balance}</Text>;
}
the current type looks like this
type Data = {
balance: number;
};
type AccountInfoCardProps = {
isLoading?: boolean;
data?: Data;
};
And i'm expecting that the below requirement to be valid and working as I expected as described in the comment
function Test() {
const dataFromApi = { balance: 100 } satisfies Data;
const isLoadingApi = 1 + 1 === 2;
return (
<>
{/* // Error: Either isLoading or data must be provided */}
<AccountInfoCard />
{/* // Error: Cannot set isLoading to false and not provide data */}
<AccountInfoCard isLoading={false} />
{/* // Valid: isLoading provided with boolean, data becomes optional */}
<AccountInfoCard isLoading={isLoadingApi} />
{/* // Valid: data is provided then isLoading becomes optional */}
<AccountInfoCard data={dataFromApi} />
{/* // Valid: Both isLoading and data are provided */}
<AccountInfoCard isLoading={isLoadingApi} data={dataFromApi} />
{/* // Valid: isLoading provided with boolean, data is optional */}
<AccountInfoCard isLoading={true} />
{/* // Valid: isLoading provided with boolean and data is also provided */}
<AccountInfoCard isLoading={isLoadingApi} data={dataFromApi} />
</>
);
}
what i have tried
type AccountInfoCardProps = {
isLoading: boolean;
data?: Data;
} | {
isLoading?: false;
data: Data;
};
I tried discriminated union like above code but one condition doesn't fulfilled which is
{/* // Error: Cannot set isLoading to false and not provide data */}
<AccountInfoCard isLoading={false} />
suppose to be error but it is not throwing any error
The solution here comes in two parts. First, you need to declare the two possible
AccountInfoCardPropsas a union, as you have done:but after doing that, simply writing
<AccountInfoCard isLoading={someBoolean} />will not work.This is because in order to let TS pick one of the members of the union, you need to prove whether the boolean is
trueorfalse:The end result looks like this: Playground