<h2>Tipos Primitivos em TypeScript</h2>
<p>Os tipos primitivos são a base de qualquer programa TypeScript. Diferentemente do JavaScript, onde você descobre o tipo em tempo de execução, TypeScript permite que você <strong>declare explicitamente</strong> qual tipo uma variável deve ter. Isso significa que o compilador vai verificar se você está usando aquela variável corretamente antes mesmo do código rodar.</p>
<p>TypeScript herda os tipos primitivos do JavaScript e adiciona segurança de tipo sobre eles. Os principais tipos primitivos são: <code>number</code>, <code>string</code>, <code>boolean</code>, <code>null</code>, <code>undefined</code>, <code>bigint</code> e <code>symbol</code>. Cada um deles tem características específicas e comportamentos bem definidos.</p>
<h3>Number</h3>
<p>O tipo <code>number</code> representa qualquer número, seja inteiro ou decimal. Ao contrário de linguagens como Java ou C#, TypeScript não diferencia entre inteiros e floats — tudo é <code>number</code>.</p>
<pre><code class="language-typescript">let idade: number = 28;
let altura: number = 1.75;
let temperatura: number = -5;
let infinito: number = Infinity;
let naoNumero: number = NaN;
// TypeScript vai reclamar disso:
let erro: number = "vinte e oito"; // Erro: Type 'string' is not assignable to type 'number'</code></pre>
<p>Você também pode trabalhar com números em diferentes bases: hexadecimal, octal e binária. Isso é útil em contextos de manipulação de bits ou trabalho com flags.</p>
<pre><code class="language-typescript">let hexadecimal: number = 0xFF; // 255
let octal: number = 0o77; // 63
let binario: number = 0b1111; // 15</code></pre>
<h3>String</h3>
<p>O tipo <code>string</code> representa texto. TypeScript aceita strings com aspas simples, duplas ou com template literals (backticks).</p>
<pre><code class="language-typescript">let nome: string = "João";
let sobrenome: string = 'Silva';
let mensagem: string = Olá, ${nome} ${sobrenome}!;
console.log(mensagem); // "Olá, João Silva!"</code></pre>
<p>Template literals são particularmente poderosos porque permitem interpolar valores dentro da string usando <code>${}</code>. Isso é muito melhor do que concatenar strings manualmente.</p>
<h3>Boolean</h3>
<p>O tipo <code>boolean</code> tem apenas dois valores possíveis: <code>true</code> e <code>false</code>. Parece simples, mas é fundamental em estruturas de controle como <code>if</code>, <code>while</code> e <code>for</code>.</p>
<pre><code class="language-typescript">let ativo: boolean = true;
let deletado: boolean = false;
let maiorDeIdade: boolean = idade >= 18;
if (maiorDeIdade) {
console.log("Pode entrar na discoteca");
}</code></pre>
<p>Um erro comum é tentar usar um valor não-booleano em um contexto que espera um boolean. TypeScript vai te proteger disso.</p>
<h3>Null e Undefined</h3>
<p><code>null</code> e <code>undefined</code> são dois tipos especiais que costumam confundir iniciantes. <strong><code>undefined</code></strong> significa que uma variável foi declarada mas não recebeu um valor. <strong><code>null</code></strong> é um valor atribuído explicitamente para representar "nada" ou "ausência de valor".</p>
<pre><code class="language-typescript">let indefinido: undefined = undefined;
let nulo: null = null;
let nome: string; // undefined por padrão
console.log(nome); // undefined
let resultado: null = null; // você atribuiu null explicitamente</code></pre>
<p>Na prática, você raramente vai anotar uma variável como <code>undefined</code> ou <code>null</code>. Mas é importante entender a diferença porque TypeScript tem recursos para lidar com ambos (como o operador de coalescência nula <code>??</code>).</p>
<h3>BigInt</h3>
<p>O tipo <code>bigint</code> permite trabalhar com números inteiros arbitrariamente grandes. Números normais têm um limite (até 2^53 - 1), mas <code>bigint</code> não tem esse limite.</p>
<pre><code class="language-typescript">let numeroGrande: bigint = 9007199254740991n;
let muitoMaior: bigint = 9007199254740992n; // note o 'n' no final
let resultado: bigint = numeroGrande + 1n;
console.log(resultado); // 9007199254740992n</code></pre>
<p>A letra <code>n</code> no final é obrigatória para indicar que é um <code>bigint</code>. Um detalhe importante: você <strong>não pode</strong> misturar <code>number</code> e <code>bigint</code> em operações.</p>
<p>---</p>
<h2>Literais em TypeScript</h2>
<p>Enquanto os tipos primitivos são genéricos (qualquer <code>string</code> é <code>string</code>), os literais são específicos. Um literal é um <strong>tipo que representa um valor exato</strong>. Por exemplo, a string <code>"João"</code> é um valor específico, e TypeScript permite criar um tipo que aceita apenas aquele valor.</p>
<h3>Literal Types</h3>
<p>Literal types são incrivelmente úteis quando você quer restringir os valores possíveis de uma variável. Ao invés de aceitar qualquer <code>string</code>, você pode aceitar apenas <code>"ativo"</code>, <code>"inativo"</code> ou <code>"pendente"</code>.</p>
<pre><code class="language-typescript">let status: "ativo" | "inativo" | "pendente" = "ativo";
status = "inativo"; // OK
status = "deletado"; // Erro: Type '"deletado"' is not assignable to type '"ativo" | "inativo" | "pendente"'</code></pre> <p>Você já deve ter visto a sintaxe <code>|</code> (union) ali — ela significa "ou". Então <code>"ativo" | "inativo" | "pendente"</code> significa "um desses três valores, não mais".</p>
<p>Essa é uma forma muito melhor de trabalhar do que simplesmente usar <code>string</code>. Se alguém passar um status inválido, o compilador vai avisar no desenvolvimento, não em produção.</p>
<h3>Numeric Literals</h3>
<p>Você também pode usar literais numéricos para representar valores exatos:</p>
<pre><code class="language-typescript">type StatusCode = 200 | 301 | 404 | 500;
let resposta: StatusCode = 200; // OK
let erro: StatusCode = 503; // Erro: Type '503' is not assignable to type 'StatusCode'</code></pre>
<h3>Boolean Literals e Combinações</h3>
<p>Existem casos onde você quer ser ainda mais específico:</p>
<pre><code class="language-typescript">type Sucesso = true;
type Falha = false;
let resultado: Sucesso = true; // OK
let resultado2: Falha = false; // OK
let resultado3: Sucesso = false; // Erro</code></pre>
<p>Você também pode combinar literais de tipos diferentes:</p>
<pre><code class="language-typescript">type Resposta = "sim" | "não" | true | 42;
let escolha: Resposta = "sim"; // OK
let outra: Resposta = true; // OK
let numero: Resposta = 42; // OK
let invalida: Resposta = false; // Erro</code></pre>
<p>---</p>
<h2>Type Inference (Inferência de Tipo)</h2>
<p>Type inference é o <strong>superpoder</strong> do TypeScript. Em muitos casos, você não precisa anotar tipos explicitamente — o compilador consegue deduzir qual tipo uma variável tem baseado no valor que você atribui a ela. Isso torna o código mais legível e reduz a quantidade de anotações.</p>
<h3>Inferência Básica</h3>
<p>Quando você cria uma variável com um valor inicial, TypeScript automaticamente infere o tipo:</p>
<pre><code class="language-typescript">let nome = "João"; // TypeScript infere: string
let idade = 28; // TypeScript infere: number
let ativo = true; // TypeScript infere: boolean
// Agora o editor vai avisar se você tentar fazer algo errado:
nome = 42; // Erro: Type 'number' is not assignable to type 'string'
idade = "vinte e oito"; // Erro: Type 'string' is not assignable to type 'number'</code></pre>
<p>Essa é a razão pela qual muitos desenvolvedores TypeScript experientes não colocam anotações em variáveis simples. A inferência já faz o trabalho.</p>
<h3>Inferência em Funções</h3>
<p>Você pode omitir a anotação do tipo de retorno em funções, e TypeScript vai inferir baseado no que você retorna:</p>
<pre><code class="language-typescript">function somar(a: number, b: number) {
return a + b; // TypeScript infere que o retorno é: number
}
const resultado = somar(5, 3); // resultado tem tipo: number
console.log(resultado); // 8</code></pre>
<p>Mesmo sem escrever <code>function somar(...): number</code>, o compilador sabe que essa função retorna um número. Porém, <strong>é uma boa prática anotar o retorno explicitamente</strong> para deixar claro para quem lê o código.</p>
<pre><code class="language-typescript">function somar(a: number, b: number): number {
return a + b;
}</code></pre>
<h3>Inferência em Arrays e Objetos</h3>
<p>Com arrays, TypeScript infere o tipo dos elementos:</p>
<pre><code class="language-typescript">let numeros = [1, 2, 3];
// TypeScript infere: number[]
numeros.push(4); // OK
numeros.push("5"); // Erro: Argument of type 'string' is not assignable to parameter of type 'number'</code></pre>
<p>Com objetos, a inferência funciona para cada propriedade:</p>
<pre><code class="language-typescript">let usuario = {
nome: "João",
idade: 28,
ativo: true
};
// TypeScript infere:
// {
// nome: string;
// idade: number;
// ativo: boolean;
// }
usuario.nome = "Maria"; // OK
usuario.idade = "trinta"; // Erro: Type 'string' is not assignable to type 'number'
usuario.email = "joao@example.com"; // Erro: Property 'email' does not exist on type...</code></pre>
<h3>Quando a Inferência Não é Suficiente</h3>
<p>Há situações onde o TypeScript não consegue inferir o tipo corretamente, e você precisa ser explícito:</p>
<pre><code class="language-typescript">// Sem inicialização, TypeScript não consegue inferir
let valor;
valor = 42;
valor = "texto"; // OK, porque TypeScript assume 'any'
// Solução: anotar o tipo
let numero: number;
numero = 42;
numero = "texto"; // Erro</code></pre>
<p>Outro caso é quando você trabalha com genéricos e valores abstratos:</p>
<pre><code class="language-typescript">function processar<T>(item: T): T {
return item; // TypeScript sabe que retorna T
}
let resultado = processar("teste"); // resultado tem tipo: string
let outro = processar(42); // outro tem tipo: number</code></pre>
<p>---</p>
<h2>Praticando: Cenário Real</h2>
<p>Vamos juntar tudo isso em um exemplo prático. Imagine que você está construindo um sistema de gerenciamento de usuários:</p>
<pre><code class="language-typescript">type StatusUsuario = "ativo" | "inativo" | "bloqueado"; type TipoPermissao = "leitura" | "escrita" | "admin";
interface Usuario {
id: number;
nome: string;
email: string;
idade: number;
ativo: boolean;
status: StatusUsuario;
permissoes: TipoPermissao[];
}
function criarUsuario(nome: string, email: string, idade: number): Usuario {
return {
id: Math.floor(Math.random() * 10000),
nome,
email,
idade,
ativo: true,
status: "ativo",
permissoes: ["leitura"]
};
}
function atualizarStatus(usuario: Usuario, novoStatus: StatusUsuario): void {
usuario.status = novoStatus;
usuario.ativo = novoStatus !== "bloqueado";
}
// Usando as funções
const usuario1 = criarUsuario("João Silva", "joao@example.com", 28);
console.log(usuario1);
// {
// id: 5432,
// nome: 'João Silva',
// email: 'joao@example.com',
// idade: 28,
// ativo: true,
// status: 'ativo',
// permissoes: [ 'leitura' ]
// }
atualizarStatus(usuario1, "bloqueado");
console.log(usuario1.ativo); // false
// Isso causaria erro:
atualizarStatus(usuario1, "suspenso"); // Erro: Argument of type '"suspenso"' is not assignable...
usuario1.status = "deletado"; // Erro: Type '"deletado"' is not assignable...</code></pre>
<p>Veja como TypeScript protege você de erros:</p>
<ol>
<li><strong>Tipos primitivos</strong> garantem que <code>idade</code> seja sempre um número</li>
<li><strong>Literal types</strong> garantem que <code>status</code> seja apenas um dos valores esperados</li>
<li><strong>Type inference</strong> permite que funções como <code>criarUsuario</code> retornem o objeto correto sem repetição de código</li>
</ol>
<p>---</p>
<h2>Conclusão</h2>
<p>Os <strong>três pilares</strong> que você deve levar para casa são:</p>
<ol>
<li><strong>Tipos Primitivos são segurança</strong>: Ao anotar uma variável como <code>number</code>, <code>string</code> ou <code>boolean</code>, você está criando um contrato. TypeScript vai verificar esse contrato e avisar se algo não se encaixa. Isso previne bugs antes que chegem em produção.</li>
</ol>
<p>2. <strong>Literal Types são precisão</strong>: Quando você usa <code>"ativo" | "inativo"</code> ao invés de apenas <code>string</code>, você está sendo específico sobre quais valores são válidos. Isso torna seu código mais robusto e autoexplicativo.</p>
<ol>
<li><strong>Type Inference é produtividade</strong>: Você não precisa anotar tudo. O TypeScript consegue deduzir tipos em muitas situações, permitindo que você escreva código limpo sem sacrificar segurança. Aprenda quando é seguro omitir anotações e quando deve ser explícito.</li>
</ol>
<p>---</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Basic Types</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Literal Types</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/type-inference.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Type Inference</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures" target="_blank" rel="noopener noreferrer">MDN Web Docs - JavaScript Data Types</a></li>
<li><a href="https://www.oreilly.com/library/view/effective-typescript/9781492053736/" target="_blank" rel="noopener noreferrer">Effective TypeScript by Dan Vanderkam</a></li>
</ul>
<p><!-- FIM --></p>