String vs string: o assassino silencioso da performance do seu código
\n\nVocê já se perguntou por que o TypeScript reclama de String em um lugar e de string em outro? Ou talvez já tenha visto desenvolvedores experientes torcerem o nariz ao ver new String("hello")? Essa é uma dúvida clássica no Stack Overflow, com milhares de visualizações. E não é à toa. A diferença entre String e string não é apenas uma peculiaridade da linguagem, mas uma distinção fundamental que impacta a performance, a segurança e a legibilidade do seu código.
Neste artigo, não vamos apenas dar uma definição superficial. Vamos explorar o que está por trás de cada um desses tipos, como eles se comportam em tempo de execução e por que a escolha certa pode acelerar sua aplicação em dezenas de por cento. Prepare-se, vai ser interessante!
\n\nPrimitivos vs Objetos: o básico dos básicos
\n\nPara entender a diferença, precisamos lembrar como o JavaScript (e, por consequência, o TypeScript) lida com dados. A linguagem tem dois tipos de valores:
\n\n- \n
- Primitivos (primitives):
string,number,boolean,null,undefined,symbol,bigint. Eles são imutáveis e armazenados por valor. \n - Objetos (objects):
Object,Array,Function, além deString(com letra maiúscula),Number,Boolean. Eles são mutáveis e armazenados por referência. \n
Quando você escreve let nome: string = "Alice", você cria um primitivo. Quando escreve let nome: String = new String("Alice"), você cria um objeto wrapper. E é aí que a coisa fica interessante.
Tipo string (com letra minúscula)
\nEste é o tipo primitivo. É usado em 99,9% dos casos. É leve, rápido e previsível. A comparação de dois primitivos é feita por valor:
\n\nlet a: string = "hello";\nlet b: string = "hello";\nconsole.log(a === b); // true\n\nO TypeScript recomenda usar exatamente este. É o padrão de facto para declarar variáveis de string, parâmetros de funções e valores de retorno.
\n\nTipo String (com letra maiúscula)
\nEste é o tipo objeto, um construtor wrapper. Ele existe desde os primórdios do JavaScript para trabalhar com métodos de string. No entanto, seu uso direto é considerado um antipadrão, a menos que você tenha uma tarefa muito específica. A comparação de dois objetos String é feita por referência:
let a: String = new String("hello");\nlet b: String = new String("hello");\nconsole.log(a === b); // false! São objetos diferentes na memória\n\nIsso pode levar a bugs difíceis de encontrar. Imagine verificar se o valor de uma string é "hello" e receber false só porque alguém usou new String().
Performance: por que string vence?
\n\nAgora vamos ao que interessa: números e otimização. Vamos escrever um teste de performance simples (você pode executá-lo no seu ambiente, como Node.js ou no navegador).
\n\n// Teste de performance\nconst iteracoes = 1_000_000;\n\nconsole.time("string (primitivo)");\nlet soma = "";\nfor (let i = 0; i < iteracoes; i++) {\n soma += "a";\n}\nconsole.timeEnd("string (primitivo)");\n\nconsole.time("String (objeto)");\nlet somaObj = new String("");\nfor (let i = 0; i < iteracoes; i++) {\n somaObj += "a"; // Aqui ocorre autoboxing e unboxing\n}\nconsole.timeEnd("String (objeto)");\n\nO resultado em uma máquina típica será drasticamente diferente (a favor do primitivo de 2 a 5 vezes). Por quê? Porque:
\n- \n
- Primitivo
string— é apenas uma sequência de caracteres na memória. As operações com ele são otimizadas no nível do motor JavaScript. \n