javascript - Typescript extensão condicional baseada no valor dos parâmetros
Sou relativamente novo em Typescript e estou tentando ter um extends condicional baseado no valor de um argumento de função. O contexto é definir um widget para seleção de objetos, que pode ser um select, uma lista de radio (um grupo de radio) ou uma lista de checkbox (um grupo de checkbox). Cada um desses diferentes widgets aceita props diferentes, name: SelectProps, RadioGroupProps e CheckboxGroupProps. A seleção do widget é baseada em dois parâmetros: expand (significa que queremos uma lista de checkbox ou radio) e multiple (significa que queremos selecionar múltiplos elementos ou não).
function SelectionWidget(props: SelectionWidgetProps) {}
Aqui, eu quero que:
se props corresponder a {expand: true, multiple: false}, então SelectionWidgetProps deve estender RadioGroupProps.
se props corresponder a {expand: true, multiple: true}, então SelectionWidgetProps deve estender CheckboxGroupProps.
caso contrário, SelectionWidgetProps deve estender SelectProps.
Eu tentei o seguinte:
type SelectTypeExtend<T> =
T extends { expand: false } ? SelectProps :
T extends { expand: true; multiple: false } ? RadioGroupProps :
T extends { expand: true; multiple: true; } ? CheckboxGroupProps :
never;
interface SelectTypeProps extends SelectTypeExtends {
multiple?: boolean;
expand?: boolean;
}
Mas isso não funciona, pois SelectTypeExtends espera um argumento. É possível conseguir isso com Typescript? Obrigado!
Resposta #1
Você pode estender interface apenas com tipos conhecidos estaticamente. Isso significa que não é permitido estender SelectTypeProps com SelectTypeExtends porque SelectTypeExtends é uma caixa preta. Ninguém sabe o tipo de T.
Para gerenciar este caso, você deve usar type em vez de 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>
Exemplo com componentes React:
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
]
};
Parque infantil
Eu modifiquei ligeiramente o tipo SelectionComponentProps. Por favor, tenha em mente que, se você não fornecer alguma propriedade para o componente React, o TS a trata como undefined e não como false, portanto, você precisa tornar algumas das suas propriedades false como partial
