I want to have a function named Control
that can be overloaded with props of two different types. This is the code I wrote. But it throws an error while trying to access specific properties of the prop.
Any idea what I'm doing wrong?
interface IControl {
title: string,
description: string,
type: 'button' | 'switch'
}
type TButtonVariant = 'contained' | 'outlined' | undefined
interface IControlWithButton extends IControl {
buttonText: string,
buttonVariant: TButtonVariant
}
interface IControlWithSwitch extends IControl {
isOn: boolean
}
type GetExactProp<T> = T extends { isOn: boolean } ? IControlWithSwitch : IControlWithButton;
function Control<T extends IControlWithButton | IControlWithSwitch>(props: GetExactProp<T>): void {
console.log(props.buttonText) // Property 'buttonText' does not exist on type 'GetExactProp<T>'
console.log(props.isOn) // Property 'isOn' does not exist on type 'GetExactProp<T>'
}
const ControlWithButtonProps: IControlWithButton = {
title: 'Ola!',
description: 'Section description',
buttonText: 'I am a button',
buttonVariant: 'contained',
type: 'button'
}
const ControlWithSwitchProps: IControlWithSwitch = {
title: 'Ola!',
description: 'Section description',
isOn: false,
type: 'switch'
}
Control(ControlWithButtonProps)
Control(ControlWithSwitchProps)
The reason why the error appears is:
No value will be assignable to an unresolved conditional type (a conditional type that still depends on a free generic type variable)
From: https://stackoverflow.com/a/52144866/12397250
That's why Typescript has no idea what type props
inside the function is. What you can do is to put a type guard in the function, and the generic isn't really necessary here
function Control(props: IControlWithButton | IControlWithSwitch): void {
if ("isOn" in props) {
props; // IControlWithSwitch
props.isOn; // boolean
props.buttonText; // ERROR
} else {
props; // IControlWithButton
props.isOn; // ERROR
props.buttonText; // string
}
}
Or create a user defined type guard:
function isControlWithSwitch(control: IControlWithButton | IControlWithSwitch): control is IControlWithSwitch {
return "isOn" in control;
}
function Control(props: IControlWithButton | IControlWithSwitch): void {
if (isControlWithSwitch(props)) {
props; // IControlWithSwitch
props.isOn; // boolean
props.buttonText; // ERROR
} else {
props; // IControlWithButton
props.isOn; // ERROR
props.buttonText; // string
}
}