Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

228
Visualizações
Typescript two object union - problem with making typescript think it's the other type

I encountered a really weird problem and I'm not sure if the problem is related to badly written types or it's a weird typescript thing.

So I have this props type:

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;
};

And then I'm trying to force typescript to read what type is being passed to onChange method.

if (isMultiSelect) {
      onChange({
        answers,
        isHeadless,
      });
    } else {
      onChange({
        answers,
        displayMode,
      });
    }

So the first onChange is correct as it shows its type as onChange: (value: SetStateAction<MultiSelectAnswerOptionsType>) => void but the second one I'd expect to be like in the types specified in props with isMultiSelect being false but instead type of 2nd onChange is as follows: onChange: (value: SetStateAction<MultiSelectAnswerOptionsType> & SetStateAction<SelectAnswerOptionsType>) => void so kinda mix of both even though from props types you would say it's either one or another.

I reckon I've done something silly here. Really appreciate all your help.

Thanks

about 4 years ago · Santiago Trujillo
1 Respostas
Responde à pergunta

0

You have defined the props to be (simplified for readability):

props: { onChange: MultiSelectDispatcher, ... } | { onChange: SelectDispatcher, ... }

That means, the props can only be the one or the other, and Typescript doesn't know which one it is, and infers that from the usage:

if( isMultiSelect ){
  onChange({ answers, isHeadless }); // <-- TS thinks: probably the "MultiSelect" one
} else {
  onChange({ answers, displayMode });  // <-- TS already decided "MultiSelect" earlier
}

So Typescript decides which type the whole props object probably is at some point, and sticks to that decision from there on.

Use a type guard (maybe)

As a solution you might be able to use type guards (maybe not this easy, see the "But ..." below), like:

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
}

But also Typescript might have inferred the type of the props even earlier, or will "suddenly" do that if you make some changes to your code later.

Define the type of onChange (if possible)

I suggest, if possible, not to define two alternatives for the props object, but instead define only one type for the props, with two alternative onChange handlers. (The React props should stay consistent anyway)

E.g:

props: {
    onChange: MultiSelectDispatcher | SelectDispatcher,
    ...
}

And also use a type guard, like:

// 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 });
}

Still problems ...

My last type guard guardMultiSelectOnChange makes another problem obvious:

We always assumed that the onChange function is the correct one if isMultiSelect is one or the other. But nothing prevents someone to pass isMultiSelect: false and the MultiSelectDispatcher.

It would be better to type guard the function signature itself, instead of just stating onChange is MultiSelectDispatcher if some other property (isMultiSelect) has some value.

But that will be a somewhat larger refactoring, outside the scope of this question, I guess.

about 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda