TypeScript

Guia Completo de Tipos Primitivos, Literais e Type Inference em TypeScript

15 min de leitura

Guia Completo de Tipos Primitivos, Literais e Type Inference em TypeScript

Tipos Primitivos em TypeScript 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ê declare explicitamente 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. TypeScript herda os tipos primitivos do JavaScript e adiciona segurança de tipo sobre eles. Os principais tipos primitivos são: , , , , , e . Cada um deles tem características específicas e comportamentos bem definidos. Number O tipo 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 é . 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. String O tipo representa texto. TypeScript aceita strings com aspas simples, duplas

<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 = &quot;vinte e oito&quot;; // Erro: Type &#039;string&#039; is not assignable to type &#039;number&#039;</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 = &quot;João&quot;;

let sobrenome: string = &#039;Silva&#039;;

let mensagem: string = Olá, ${nome} ${sobrenome}!;

console.log(mensagem); // &quot;Olá, João Silva!&quot;</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 &gt;= 18;

if (maiorDeIdade) {

console.log(&quot;Pode entrar na discoteca&quot;);

}</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 &quot;nada&quot; ou &quot;ausência de valor&quot;.</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 &#039;n&#039; 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>&quot;João&quot;</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>&quot;ativo&quot;</code>, <code>&quot;inativo&quot;</code> ou <code>&quot;pendente&quot;</code>.</p>

<pre><code class="language-typescript">let status: &quot;ativo&quot; | &quot;inativo&quot; | &quot;pendente&quot; = &quot;ativo&quot;;

status = &quot;inativo&quot;; // OK

status = &quot;deletado&quot;; // Erro: Type &#039;&quot;deletado&quot;&#039; is not assignable to type &#039;&quot;ativo&quot; | &quot;inativo&quot; | &quot;pendente&quot;&#039;</code></pre> <p>Você já deve ter visto a sintaxe <code>|</code> (union) ali — ela significa &quot;ou&quot;. Então <code>&quot;ativo&quot; | &quot;inativo&quot; | &quot;pendente&quot;</code> significa &quot;um desses três valores, não mais&quot;.</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 &#039;503&#039; is not assignable to type &#039;StatusCode&#039;</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 = &quot;sim&quot; | &quot;não&quot; | true | 42;

let escolha: Resposta = &quot;sim&quot;; // 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 = &quot;João&quot;; // 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 &#039;number&#039; is not assignable to type &#039;string&#039;

idade = &quot;vinte e oito&quot;; // Erro: Type &#039;string&#039; is not assignable to type &#039;number&#039;</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(&quot;5&quot;); // Erro: Argument of type &#039;string&#039; is not assignable to parameter of type &#039;number&#039;</code></pre>

<p>Com objetos, a inferência funciona para cada propriedade:</p>

<pre><code class="language-typescript">let usuario = {

nome: &quot;João&quot;,

idade: 28,

ativo: true

};

// TypeScript infere:

// {

// nome: string;

// idade: number;

// ativo: boolean;

// }

usuario.nome = &quot;Maria&quot;; // OK

usuario.idade = &quot;trinta&quot;; // Erro: Type &#039;string&#039; is not assignable to type &#039;number&#039;

usuario.email = &quot;joao@example.com&quot;; // Erro: Property &#039;email&#039; 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 = &quot;texto&quot;; // OK, porque TypeScript assume &#039;any&#039;

// Solução: anotar o tipo

let numero: number;

numero = 42;

numero = &quot;texto&quot;; // Erro</code></pre>

<p>Outro caso é quando você trabalha com genéricos e valores abstratos:</p>

<pre><code class="language-typescript">function processar&lt;T&gt;(item: T): T {

return item; // TypeScript sabe que retorna T

}

let resultado = processar(&quot;teste&quot;); // 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 = &quot;ativo&quot; | &quot;inativo&quot; | &quot;bloqueado&quot;; type TipoPermissao = &quot;leitura&quot; | &quot;escrita&quot; | &quot;admin&quot;;

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: &quot;ativo&quot;,

permissoes: [&quot;leitura&quot;]

};

}

function atualizarStatus(usuario: Usuario, novoStatus: StatusUsuario): void {

usuario.status = novoStatus;

usuario.ativo = novoStatus !== &quot;bloqueado&quot;;

}

// Usando as funções

const usuario1 = criarUsuario(&quot;João Silva&quot;, &quot;joao@example.com&quot;, 28);

console.log(usuario1);

// {

// id: 5432,

// nome: &#039;João Silva&#039;,

// email: &#039;joao@example.com&#039;,

// idade: 28,

// ativo: true,

// status: &#039;ativo&#039;,

// permissoes: [ &#039;leitura&#039; ]

// }

atualizarStatus(usuario1, &quot;bloqueado&quot;);

console.log(usuario1.ativo); // false

// Isso causaria erro:

atualizarStatus(usuario1, &quot;suspenso&quot;); // Erro: Argument of type &#039;&quot;suspenso&quot;&#039; is not assignable...

usuario1.status = &quot;deletado&quot;; // Erro: Type &#039;&quot;deletado&quot;&#039; 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>&quot;ativo&quot; | &quot;inativo&quot;</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>&lt;!-- FIM --&gt;</p>

Comentários

Mais em TypeScript

Boas Práticas de React Query com TypeScript: Queries, Mutations e Tipos Inferidos para Times Ágeis
Boas Práticas de React Query com TypeScript: Queries, Mutations e Tipos Inferidos para Times Ágeis

Por que React Query Revolucionou o Gerenciamento de Estado Durante minha carr...

Dominando Classes em TypeScript: Modificadores, Readonly e Parameter Properties em Projetos Reais
Dominando Classes em TypeScript: Modificadores, Readonly e Parameter Properties em Projetos Reais

Introdução aos Modificadores de Acesso em TypeScript Os modificadores de aces...

Componentes Genéricos em React com TypeScript: Do Básico ao Avançado
Componentes Genéricos em React com TypeScript: Do Básico ao Avançado

O que são Componentes Genéricos em React com TypeScript? Componentes genérico...