Typescript conditional extends based on parameters value
You can extend interface
only with statically known types. It means that does not allow you to extend SelectTypeProps
with SelectTypeExtends
because SelectTypeExtends
is a black box. Nobody know type of T
.
In order to manage this case, you should use type
instead of interface
:
type SelectProps = {
tag: 'SelectProps'
}
type RadioGroupProps = {
tag: 'RadioGroupProps'
}
type CheckboxGroupProps = {
tag: 'CheckboxGroupProps '
}
type SelectTypeExtend<T> =
T extends { expand: false } ? SelectProps :
T extends { expand: true; multiple: false } ? RadioGroupProps :
T extends { expand: true; multiple: true; } ? CheckboxGroupProps :
never;
type SelectTypeProps<T> = { // <------ type instead of interface
multiple?: boolean;
expand?: boolean;
} & SelectTypeExtend<T>
Example with react components:
import React from "react";
import {
Select,
SelectProps,
Checkbox,
CheckboxGroupProps,
Radio,
RadioGroupProps
} from "formik-antd";
export type SelectionComponentProps =
| { expand?: false } & { options: SelectProps }
| { expand: true; multiple?: false } & { options: RadioGroupProps }
| { expand: true; multiple: true } & { options: CheckboxGroupProps };
function SelectionComponent(props: SelectionComponentProps) {
if (props.expand) {
if (props.multiple) {
return <Checkbox.Group {...props.options} />;
} else {
return <Radio.Group {...props.options} />;
}
}
return <Select {...props.options} />;
};
const select: SelectProps = 'unimplemented' as any;
const radio: RadioGroupProps = 'unimplemented' as any;
const checkbox: CheckboxGroupProps = 'unimplemented' as any;
export const TestRadio = () => {
// Accept properties from RadioGroupProps but not from SelectProps or CheckboxGroupProps
return [
<SelectionComponent expand options={radio} />, // ok
<SelectionComponent expand options={select} />, // expected error
<SelectionComponent expand options={checkbox} />, // expected error
]
};
export const TestCheckbox = () => {
// Accept properties from CheckboxProps but not from SelectProps or RadioGroupProps
return [
<SelectionComponent expand multiple options={checkbox} />, // ok
<SelectionComponent expand multiple options={select} />, // expected error
<SelectionComponent expand multiple options={radio} /> // expected error
]
};
export const TestSelect = () => {
// Accept properties from SelectProps but not from RadioGroupProps or CheckboxGroupProps
return [
<SelectionComponent options={select} />, // ok
<SelectionComponent options={radio} />, // expected error
<SelectionComponent options={checkbox} />, // expected error
]
};
Playground
I have slightly modified SelectionComponentProps
type. Please keep in mind, that if you don't provide some property to react component, TS treats it as undefined
and not as false
, hence you need to make some of your false
properties as a partial