Inferência de tipos
Outro aspecto bastante importante com relação à tipos em linguagens de programação está no momento em que o tipo de uma variável é determinado: em tempo de compilação (em linguagens estáticas) ou durante a execução (em linguagens dinâmicas).
🪨 Estática
Na tipagem estática, o tipo de cada variável e expressão é determinado em tempo de compilação e não pode mudar ao longo do tempo de execução. Linguagens como C, Java e Rust são estaticamente tipadas.
int variavel; // variavel será int sempre
No exemplo acima, o tipo foi declarado explicitamente pelo programador. Algumas linguagens estaticamente tipadas, no entanto, oferecem também inferência de tipos: o compilador deduz o tipo da variável a partir do valor atribuído, sem que seja necessário declará-lo. Em TypeScript, por exemplo:
let variavel = 10; // o compilador infere que variavel é do tipo number
Mesmo sem a anotação explícita : number, o TypeScript sabe que variavel é numérica e continuará exigindo esse tipo dali em diante — diferente do C, que não possui inferência de tipos e sempre exige a declaração explícita.
Seja com tipo explícito ou inferido, a tipagem estática proporciona uma série de vantagens, como melhor desempenho em tempo de execução, menor consumo de recursos, e mais segurança no código, uma vez que muitos erros podem ser captados durante a compilação.
Por exemplo, a linguagem Rust é conhecida por sua segurança de memória, que é em grande parte garantida durante a fase de compilação.
⚡ Dinâmica
Por outro lado, na tipagem dinâmica o tipo de uma variável só é conhecido em tempo de execução, sendo deduzido a partir do valor atribuído a cada momento — podendo, inclusive, mudar ao longo da execução. Linguagens como Python, JavaScript e Ruby são exemplos de linguagens que adotam essa abordagem. Veja um exemplo em Python:
variavel = "ufmt"
variavel = 10
A inferência de tipos dinâmica oferece maior flexibilidade e rapidez no desenvolvimento, pois permite aos desenvolvedores escrever código sem se preocupar explicitamente com a definição de tipos.
Isso pode facilitar a prototipagem rápida e a execução de mudanças em tempo real, mas também pode levar a erros em tempo de execução que seriam evitados com a inferência de tipos estática.
Por exemplo, em JavaScript, o tipo de uma variável pode mudar ao longo de sua vida útil no código, o que pode introduzir bugs se não for devidamente gerenciado.
Vejamos um exemplo:
function calcularTotal(a, b, c) {
return a + b + c;
}
// Exemplo de uso esperado:
console.log(calcularTotal(10, 20, 30)); // Saída esperada: 60
// Chamada com tipos misturados, potencialmente por engano:
console.log(calcularTotal(10, "20", 30)); // Saída: "102030"
Neste caso, a função calcularTotal é chamada com um array que inclui uma string ("20"). Em JavaScript, o operador + é sobrecarregado para servir tanto para soma quanto para concatenação de strings.
Quando o interpretador encontra um número seguido de uma string durante a adição, ele converte todos os números subsequentes em strings e realiza a concatenação ao invés de uma soma numérica. Isso resulta na saída inesperada de "102030".