I have a question regarding interfaces:
Basically I'm creating a dynamic component that change depending on which page it is. My interface is constructed like so:
interface Props {
heading: string;
description: string;
signUp?: boolean;
signUpButton1?: string;
signUpButtonLink1?: string;
signUpButton2?: string;
signUpButtonLink2?: string;
startFreeAccount?: boolean;
startFreeAccountSignUpButton?: string;
startFreeAccountLink1?: string;
startFreeAccountSignUpLink1?: string;
startFreeAccountSignUpLink2?: string;
startFreeAccountSignUpLink3?: string;
contactUsForDemo?: boolean;
contactUsForDemoInput1?: string;
contactUsForDemoInput2?: string;
contactUsForDemoInput3?: string;
contactUsForDemoButtonText?: string;
ThanksForDemo?: boolean;
ThanksForDemoButton?: string;
}
In my code I then use for example:
{props.signUp &&
<div className="mt-10 flex flex-col items-center gap-x-6">
<Link
href={"/signUpPractitioner/startFreeAccount"}
className=" bg-black mb-4 -ml-[14vw] px-16 py-1.5 text-base font-semibold leading-7 text-white shadow-sm outline outline-offset-0 outline-black"
>
{props.signUpButton1}
</Link>
<Link href={"/practitioner/contactUsForDemo"}
className=" bg-white -ml-[14vw] px-10 py-1.5 text-base font-semibold leading-7 text-black outline outline-offset-0 outline-black shadow-sm"
>
{props.signUpButton2} <span aria-hidden="true">→</span>
</Link>
</div> }
Setting "signUp == true" would display the information in the above part of the component. My issue is that Link (from NEXTJS) cannot handle the fact that "signUpButtonLink1" might be undefined. If I put that "signUpButtonLink1" has to have a value, then even if I don't use the "signUp" part of the component, I still need to assign some value to "signUpButtonLink1" which obviously doesnt make sense (it makes sense in terms of how the current interface is constructed but not in terms of functionality).
My question is: Is it possible to set that "signUpButton1, signUpLink1 etc etc" only needs to be set if "signUp === true" ? It the same for all bools in the interface and the props "affiliated" with it.
Thanks for your help!
You can do this with simple unions
type Props = {
heading: string;
description: string;
} & (
| {
signUp?: false
signUpButton1?: never
// ...
}
| {
signUp: true
signUpButton1: string
// ...
}
) & (
| {
startFreeAccount?: false;
startFreeAccountSignUpButton?: never;
// ...
}
| {
startFreeAccount: true;
startFreeAccountSignUpButton: string;
// ...
}
) & (
...
)
Maybe add a utility type:
type OptionalProps<K extends string, T> =
| { [key in K]?: false } & { [key in keyof T]?: never }
| { [key in K]: true } & T
type Props = {
heading: string
description: string
}
& OptionalProps<'signUp', {
signUpButton1: string,
}>
& OptionalProps<'startFreeAccount', {
startFreeAccountSignUpButton: string
}>
& ...
The properties with ?: never
are not necessary, but they will make your life easier if you destructure props into individual variables. Without ?: never
you won't be able to do it, it will tell that property with name ...
doesn't exist in the object.
Or you can just use objects instead of inline props:
type Props = {
heading: string
description: string
signUp?: {
button1: string
button2: string
// ...
}
startFreeAccount?: {
signUpButton: string
// ...
}
// ...
}
I would even argue that these objects is easier to read and use