Encontré un problema realmente extraño y no estoy seguro de si el problema está relacionado con tipos mal escritos o es algo raro en el mecanografiado.
Así que tengo este tipo de accesorios:
type SelectAnswersProps = ( | { onChange: Dispatch<SetStateAction<MultiSelectAnswerOptionsType | null>>; value: MultiSelectAnswerOptionsType; isMultiSelect: true; } | { onChange: Dispatch<SetStateAction<SelectAnswerOptionsType | null>>; value: SelectAnswerOptionsType; isMultiSelect: false; } ) & { displayMode?: SelectAnswerOptionsType['displayMode']; disabled?: boolean; }; Y luego estoy tratando de forzar el mecanografiado para leer qué tipo se pasa al método onChange .
if (isMultiSelect) { onChange({ answers, isHeadless, }); } else { onChange({ answers, displayMode, }); } Entonces, el primer onChange es correcto ya que muestra su tipo como onChange: (value: SetStateAction<MultiSelectAnswerOptionsType>) => void pero el segundo espero que sea como en los tipos especificados en props con isMultiSelect siendo false pero en cambio tipo de El segundo onChange es el siguiente: onChange: (value: SetStateAction<MultiSelectAnswerOptionsType> & SetStateAction<SelectAnswerOptionsType>) => void , por lo que es una especie de combinación de ambos aunque, según los tipos de accesorios, diría que es uno u otro.
Creo que he hecho algo tonto aquí. Realmente aprecio toda su ayuda.
Gracias
Ha definido que los props sean (simplificados para facilitar la lectura) :
props: { onChange: MultiSelectDispatcher, ... } | { onChange: SelectDispatcher, ... } Eso significa que los props solo pueden ser uno u otro, y Typescript no sabe cuál es, y lo infiere del uso:
if( isMultiSelect ){ onChange({ answers, isHeadless }); // <-- TS thinks: probably the "MultiSelect" one } else { onChange({ answers, displayMode }); // <-- TS already decided "MultiSelect" earlier } Por lo tanto, Typescript decide qué tipo es probablemente el objeto de props completo en algún momento, y se apega a esa decisión a partir de ese momento.
Como solución, es posible que pueda usar protectores de tipo (tal vez no sea tan fácil, consulte el "Pero ..." a continuación) , como:
const guardMultiSelectProps = ( theProps: unknown ): theProps is MultiSelectProps => { return theProps.isMultiSelect; } if( guardMultiSelectProps( theProps ) ){ onChange({ answers, isHeadless }); // <-- TS thiks: probably the "MultiSelect" one } else { onChange({ answers, displayMode }); // <-- TS already decided "MultiSelect" earlier }Pero también Typescript podría haber inferido el tipo de accesorios incluso antes, o "repentinamente" lo hará si realiza algunos cambios en su código más adelante.
Sugiero, si es posible, no definir dos alternativas para el objeto props , sino definir solo un tipo para props , con dos controladores onChange alternativos. (Los accesorios de React deberían ser consistentes de todos modos)
P.ej:
props: { onChange: MultiSelectDispatcher | SelectDispatcher, ... }Y también use un protector de tipo, como:
// Note: This type guard works, but is not inherently type safe. const guardMultiSelectOnChange = ( onChange: unknown, multiSelect: boolean ): onChange is MultiSelectDispatcher => { return multiSelect; } if( guardMultiSelectOnChange( onChange, isMultiSelect )) { onChange({ answers, isHeadless }); } else { onChange({ answers, displayMode }); } Mi último tipo de guardia guardMultiSelectOnChange hace que otro problema sea obvio:
Siempre asumimos que la función onChange es la correcta si isMultiSelect es una u otra. Pero nada impide que alguien pase isMultiSelect: false y MultiSelectDispatcher .
Sería mejor proteger la firma de la función en sí, en lugar de simplemente indicar que onChange is MultiSelectDispatcher si alguna otra propiedad ( isMultiSelect ) tiene algún valor.
Pero supongo que será una refactorización algo mayor, fuera del alcance de esta pregunta.