Aspectos de programação

Inferência de tipos

Além da tipagem fraca ou forte, as linguagens de programação podem variar quanto à abordagem utilizada para identificar o tipo de dado de uma variável.

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".