Type narrowing e type widening no typescript

type Narrowing = "Alves";
 
type Widening = string;

Cansado de ouvir os termostype narrowingetype wideningsem entender bem o que significam? Neste artigo, você compreenderá esses importantes conceitos e verá exemplos práticos de cada caso.

Type Narrowing

Para entender ambos os conceitos, comecemos com o seguinte cenário: imagine que alguém lhe disse:"Vou lhe dar um carro de presente".

Observe que "um carro" pode ser qualquer carro. Você não sabe a cor, marca, modelo, se é elétrico, ou mesmo se é um carro real ou de brinquedo.

Agora, considere esta frase:"Vou lhe dar um carro de controle remoto, uma ferrari vermelha".

Perceba que agora temos um conjunto de possibilidades muito menor. Você tem muito mais informações sobre o tal carro.

Tornar uma informação ampla em uma informação mais específica, isso énarrowing.

Type Widening

Type widening é justamente o contrário de type narrowing.

Agora, imagine a seguinte situação:"O carro que lhe dei de presente quebrou, mas vou lhe dar outro".

Note que antes tínhamos uma ferrari vermelha de controle remoto. Agora, novamente, não sabemos que carro é esse. Será a mesma ferrari? Ainda será uma ferrari? A cor ainda será vermelha? Ainda será de controle remoto?

Tornar uma informação específica em uma informação mais ampla, isso é widening.

Narrowing e Widening na prática

Vejamos este exemplo: temos uma função que recebe um parâmetro que pode ser uma string ou um número.

function doSomething(value: string | number) {
    return value.toUpperCase();
}

Se usássemos métodos de string em um número, teríamos um erro em runtime. O que precisamos aqui é reduzir o conjunto de possibilidades. Só iremos usar um método de string se esse parâmetro for uma string, tornando o código seguro. Precisamos fazertype narrowing.

function doSomething(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase();
  }
  return "Não foi possível converter para uppercase";
}

Agora, vamos entender o que é type widening. Observe este outro exemplo: declaramos uma variável com o valor "7" usando a keyword const. Após isso, podemos observar um comportamento interessante: o typescript inferiu que o tipo dessa variável é exatamente "7".

const value = "7"; // tipo inferido como "7"

Se pararmos para analisar, faz sentido, não? Variáveis declaradas com const não podem ter seu valor reatribuído. Esse valor nunca vai mudar, então foi possível inferir "7".

Mas quando declaramos a variável com let, o typescript não infere o tipo exatamente como "7". Em vez disso, ele infere o tipo como string. E novamente, faz sentido, não? Podemos reatribuir o valor de uma variável declarada com let.

let value = "7"; // tipo inferido como string

Antes tínhamos algo mais específico, agora temos algo mais amplo. O que antes era somente a string "alves" agora pode ser qualquer string. O typescript feztype widening.

Inferência de tipos em variáveis declaradas com "let"

Talvez você esteja se perguntando: mas não pode ser qualquer coisa? Em vez de string, o tipo não deveria ser any? Ou unknown? Porque eu posso reatribuir para qualquer coisa, certo?

Sim, você está certo, mas nesse caso, o typescript tenta ser seu amigo. O tipo será o mais abrangente possível, mas baseado no valor inicial da variável.

Se você realmente quer que seja qualquer coisa, e que possa reatribuir o valor da sua variável que antes era uma string para um número, por exemplo, você precisa explicitamente declarar os possíveis tipos da variável.

let value: string | number = "7";
 
value = 7;

Com isso, chegamos ao fim deste artigo. Espero que tenha ficado claro o que significa cada termo e como isso é aplicado no typescript. Compartilhe este artigo se ele foi útil, até uma próxima:)

Playground com os exemplos