Discriminate Types
Uma união de tipos discriminados é quando você usa análise
de fluxo de código para reduzir um conjunto de objetos
para um objeto específico.
Esse padrão funciona muito bem para conjuntos de objetos
semelhantes com propriedades em comum, por exemplo: uma
lista de eventos nomeados, ou um conjunto de objetos
versionados.
// Quando o evento chega na função abaixo, ele pode ser
qualquer um dos dois tipos possíveis.
type EventoCronometrado = { nome: "inicio"; usuarioIniciou: boolean } | { nome: "encerrado"; duracao: number };
// Também podemos usar números como o discriminador, seguindo
o mesmo padrão.
Nesse exemplo, temos uma união discriminada e um estado
de erro adicional para tratar.
const tratarEvento = (evento: EventoCronometrado) => {
// Usando um switch com evento.nome, a análise de fluxo de
// código do TypeScript consegue determinar que um objeto
// pode ser representado somente por um tipo da união.
switch (evento.nome) {
case "inicio":
// Isso significa que você pode acessar usuarioIniciou
// com segurança, porque esse é o único tipo dentro de
// EventoCronometrado onde nome é "inicio".
const usuarioIniciou = evento.usuarioIniciou;
break;
case "encerrado":
const intervaloDeTempo = evento.duracao;
break;
}
};
// É recomendado utilizar um bloco switch no lugar de um
bloco if porque assim você pode garantir que todas as
partes da união são checadas. Existe um bom padrão para
isso usando o tipo never no manual (em inglês):
https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
type RespostasAPI = { versao: 0; mensagem: string } | { versao: 1; mensagem: string; status: number } | { erro: string };
const tratarResposta = (resposta: RespostasAPI) => {
// Tratar o caso de erro, e então retornar.
if ("erro" in resposta) {
console.error(resposta.erro);
return;
}
// O TypeScript agora sabe que respostas não pode ser do
// tipo erro. Caso fosse, a função teria retornado. Você
// pode verificar isso passando o mouse sobre resposta no
// trecho abaixo.
if (resposta.versao === 0) {
console.log(resposta.mensagem);
} else if (resposta.versao === 1) {
console.log(resposta.status, resposta.mensagem);
}
};