Estudo de Typescript aplicado a React¶
Este documento tem o objetivo de fornecer as anotações sobre o uso do TypeScript ou Tsx aplicado á web utilizando React como framework.
Pre-requisitos¶
- Git Instalado
- Node.js Instalado (versão LTS)
- Conhecimento básico de Javascript
- Ambiente de Desenvolvimento Integrado (IDE)
Instalação¶
- Abra o git bash, ou o terminal, e verifique a sua versão do node.js
$ node -v
Deve retornar a versão instalada mais recente, caso retorne um erro, no Linux rode o comando a baixo. No Windows, baixe o node.js no site oficial
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
-
Com o node.js instalado corretamente, abra o git bash ou o terminal e digite o comando:
$ npm install -g typescript -
Se for desejado instalar a linguagem localmente em um projeto, vá para o diretório do seu projeto e instale o Typescript como uma dependência:
$ npm install --save-dev typescript -
(Opicional) Inicialize a configuração do Typescript, dessa forma é possível configurar o comportamento do compilador
$ npx tsc --init
Configuração do tsconfig.json para React¶
Para projetos React com TypeScript, o tsconfig.json define como o compilador interpreta seu código. Um exemplo básico de configuração:
{
"compilerOptions": {
"target": "ES6", // Versão do JavaScript de saída
"lib": ["DOM", "DOM.Iterable", "ESNext"], // Bibliotecas disponíveis
"allowJs": true, // Permitir arquivos .js
"skipLibCheck": true, // Ignorar checagem de bibliotecas externas
"esModuleInterop": true, // Importações compatíveis com CommonJS
"allowSyntheticDefaultImports": true, // Importação padrão de módulos
"strict": true, // Habilitar checagem estrita de tipos
"forceConsistentCasingInFileNames": true,// Evitar conflitos de maiúsculas/minúsculas
"module": "ESNext", // Tipo de módulo
"moduleResolution": "Node", // Resolução de módulos
"resolveJsonModule": true, // Importar arquivos JSON
"isolatedModules": true, // Necessário para projetos React
"noEmit": true, // Não emitir arquivos .js (usado com Babel/React Scripts)
"jsx": "react-jsx" // Transformação JSX (React 17+)
},
"include": ["src"], // Pastas incluídas na compilação
"exclude": ["node_modules"] // Pastas excluídas
}
Integração com Bibliotecas e APIs Externas¶
Em aplicações React modernas, é comum precisar integrar bibliotecas externas (como Axios, Lodash, Moment.js) ou consumir APIs externas (REST ou GraphQL).
O TypeScript ajuda garantindo tipos corretos para dados e funções, evitando erros e facilitando o autocompletar.
🔹 Integração com Bibliotecas Externas¶
1. Instalando bibliotecas¶
Exemplo com Axios para requisições HTTP:
npm install axios
npm install --save-dev @types/axios
Muitas bibliotecas já vêm com tipos embutidos. Se não tiver, podemos usar @types/nome-da-biblioteca.
2. Uso com tipagem¶
import axios from "axios";
import React, { useEffect, useState } from "react";
type Usuario = {
id: number;
nome: string;
email: string;
};
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState<Usuario[]>([]);
useEffect(() => {
axios.get<Usuario[]>("https://jsonplaceholder.typicode.com/users")
.then(res => setUsuarios(res.data))
.catch(err => console.error(err));
}, []);
return (
<ul>
{usuarios.map(u => (
<li key={u.id}>{u.nome} - {u.email}</li>
))}
</ul>
);
}
Tipar a resposta da API (axios.get
) evita erros de acesso a propriedades inexistentes.
🔹 Integração com APIs externas¶
1. Fetch API com tipagem¶
import React, { useEffect, useState } from "react";
type Post = {
id: number;
title: string;
body: string;
};
function ListaPosts() {
const [posts, setPosts] = useState<Post[]>([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => res.json())
.then((data: Post[]) => setPosts(data))
.catch(err => console.error(err));
}, []);
return (
<div>
{posts.map(p => (
<div key={p.id}>
<h3>{p.title}</h3>
<p>{p.body}</p>
</div>
))}
</div>
);
}
Use try/catch ou .catch para tratar erros e evitar crash da aplicação.
2. Criando um Hook para Requisições Reutilizáveis¶
import { useState, useEffect } from "react";
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then(res => res.json())
.then((res: T) => setData(res))
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
// Uso do hook
type Produto = { id: number; nome: string; preco: number };
function ListaProdutos() {
const { data: produtos, loading, error } = useFetch<Produto[]>("https://api.exemplo.com/produtos");
if (loading) return <p>Carregando...</p>;
if (error) return <p>Erro: {error}</p>;
return (
<ul>
{produtos?.map(p => (
<li key={p.id}>{p.nome} - R$ {p.preco}</li>
))}
</ul>
);
}
🔹 Boas Práticas¶
- Sempre tipar a resposta da API com type ou interface.
- Tratar erros usando try/catch ou .catch.
- Criar hooks reutilizáveis para requisições frequentes.
- Usar bibliotecas com tipos embutidos ou @types/....
- Evitar manipular dados da API diretamente sem checagem de tipos.
Tipos básicos e Avançados¶
🔹 Tipos básicos¶
A seguir, segue os tipos mais comuns, utilizados no dia a dia
1. String - Textos¶
let nome: string = "Mario";
2. Number - Inteiros e Decimais¶
let idade: number = 15;
3. Boolean - Verdadeiro ou Falso¶
let ativo: boolean = true;
4. Null e Unidentified¶
vazio: null = null;
let indefinido: unidentified = unidentified;
5. Any - Aceita qualquer tipo¶
let dado: any = "texto";
dado = 10; \\válido
6. Unknown - Similar ao any mas requer checagem de tipo antes de usar¶
let valor: unknown = "texto";
if(typeof valor == "string") {
console.log(valor.toUpperCase());
}
7. Void - Usado em funções que não retornam valor¶
function: logar(): void {
console.log("Executando...")
};
8. Object - Qualquer objeto não primitivo¶
let pessoa: object = { nome: "Ana", idade: 22 };
9. Never - Funções que nunca retornam (lançam erro ou loop infinito)¶
function erro(msg: string): never {throw new Error(msg);}
10. Array¶
let numeros: number[] = [1, 2, 3];
let nomes: Array<string> = ["Ana", "João"];
11. Tupla - Array com tamanho e tipos fixos¶
let tupla: [string, number] = ["idade", 30];
🔹 Tipos Avançados¶
1. Interface - Contrato para objetos¶
interface Usuario {nome: string; idade: number;}
let u: Usuario = { nome: "Carlos", idade: 30 };
2. Enum – Conjunto de valores nomeados¶
enum Cores {Vermelho, Verde,Azul}
let cor: Cores = Cores.Verde;
3. Union (|) – Variável pode ter múltiplos tipos¶
let id: number | string; id = 123;
id = "ABC123";
4. Intersection (&) – Combina tipos¶
type Pessoa = { nome: string };
type Funcionario = { salario: number }; type Empregado = Pessoa & Funcionario;
let joao: Empregado = { nome: "João", salario: 5000 };
5. Type Aliases – Apelidos para tipos complexos¶
type ID = string | number;
let userId: ID = 42;
6. Generics – Tipos genéricos reutilizáveis¶
function identidade<T>(valor: T): T {return valor;}
let numero = identidade<number>(10);
let texto = identidade<string>("Oi");
7. Literal types – Restringe valores possíveis¶
let direcao: "norte" | "sul" | "leste" | "oeste";
direcao = "norte";
8. Optional e Nullable¶
function ola(nome?: string) {
console.log("Olá " + (nome ?? "visitante"));
}
9. Type assertion – forçar tipo¶
let valorDesconhecido: unknown = "texto";
let tamanho: number = (valorDesconhecido as string).length;
10. Mapped type – Criar tipos a partir de outros¶
UsuarioBase = { nome: string; idade: number };
type Parcial<T> = { [K in keyof T]?: T[K] };
let u1: Parcial<UsuarioBase> = { nome: "João" };
11. Utility types – utilitários prontos do¶
interface Pessoa {nome: string; idade: number; email?: string}
let p1: Partial<Pessoa> = { nome: "Lucas" };// todos opcionais
let p2: Required<Pessoa> = { nome: "Ana", idade: 20, email: "a@b.com" }; // todos obrigatórios
let p3: Readonly<Pessoa> = { nome: "Marcos", idade: 30 }; // somente leitura
let p4: Pick<Pessoa, "nome"> = { nome: "Sofia" }; // pega apenas "nome"
let p5: Omit<Pessoa, "email"> = { nome: "Leo", idade: 22 }; // remove "email"
Tipagem de props e estados em React com Tsx¶
No React com TypeScript, tipar props e estados é essencial para garantir que os componentes recebam e manipulem os dados corretos, evitando erros em tempo de compilação.
🔹 Tipagem de Props¶
Props são propriedades que um componente recebe de outro componente pai.
A tipagem das props define quais dados e tipos são esperados, permitindo autocompletar e checar tipos.
1. Usando type¶
type BotaoProps = {
texto: string;
onClick: () => void;
};
function Botao({ texto, onClick }: BotaoProps) {
return <button onClick={onClick}>{texto}</button>;
}
<Botao texto="Clique aqui" onClick={() => alert("Clicou!")} />;
2. Usando interface¶
interface CardProps {
titulo: string;
conteudo: string;
destacado?: boolean; // opcional
}
function Card({ titulo, conteudo, destacado = false }: CardProps) {
return (
<div style={{ border: destacado ? "2px solid red" : "1px solid gray" }}>
<h3>{titulo}</h3>
<p>{conteudo}</p>
</div>
);
}
<Card titulo="Nota" conteudo="Exemplo de card" />;
3. Props com children¶
type LayoutProps = {
children: React.ReactNode;
};
function Layout({ children }: LayoutProps) {
return <div className="layout">{children}</div>;
}
<Layout>
<h1>Olá Mundo</h1>
</Layout>;
🔹 Tipagem de Estado useState¶
O estado é um dado interno do componente que pode mudar ao longo do tempo. Tipar o estado garante que você atribua valores compatíveis.
1. Estado Simples¶
import { useState } from "react";
function Toggle() {
// Estado booleano simples
const [visivel, setVisivel] = useState<boolean>(false);
return (
<div>
<button onClick={() => setVisivel(!visivel)}>
{visivel ? "Ocultar" : "Mostrar"} Mensagem
</button>
{visivel && <p>🎉 Agora você está vendo esta mensagem!</p>}
</div>
)
}
export default Toggle;
2. Estado com objeto¶
type Usuario = {
nome: string;
idade: number;
};
function Perfil() {
const [usuario, setUsuario] = useState<Usuario | null>(null);
return (
<div>
<button
onClick={() => setUsuario({ nome: "Ana", idade: 25 })}
>
Carregar Usuário
</button>
{usuario && <p>{usuario.nome} - {usuario.idade} anos</p>}
</div>
);
}
3. Estado com Array tipado¶
function Lista() {
const [itens, setItens] = useState<string[]>([]);
return (
<div>
<button onClick={() => setItens([...itens, "Novo item"])}>
Adicionar
</button>
<ul>
{itens.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
🔹 Dicas¶
- Sempre prefira type ou interface para descrever props e estados complexos.
- Use union types para restringir valores possíveis em props.
- Tipar o useState ajuda a evitar estados undefined inesperados.
- Para children, use React.ReactNode.
- Para eventos, utilize os tipos do React:
React.ChangeEvent<HTMLInputElement>
React.MouseEvent<HTMLButtonElement>
React.FormEvent<HTMLFormElement>
Funções e Generics em TypeScript¶
🔹 Funções¶
No tsx, as funções funcionam da mesma maneira que no Javascript porém permite a tipagem da entrada e do returno da função.
1. Declaração de função com tipagem¶
function soma(a: number, b: number): number {
return a + b;
}
let resultado = soma(2, 3); // 5
2. Funções anônimas (arrow function)¶
const multiplicar = (x: number, y: number): number => {
return x * y;
};
3. Parâmetros opcionais¶
function saudacao(nome: string, saudacao?: string): string {
return `${saudacao ?? "Olá"}, ${nome}`;
}
saudacao("Ana"); // "Olá, Ana"
saudacao("Ana", "Bem-vinda"); // "Bem-vinda, Ana"
4. Parâmetros com valor padrão¶
function potencia(base: number, expoente: number = 2): number {
return base ** expoente;
}
potencia(3); // 9
potencia(2, 3); // 8
5. Funções que não retornam valor (void)¶
function logar(mensagem: string): void {
console.log(mensagem);
}
6. Funções que nunca retornam (never)¶
function erro(mensagem: string): never {
throw new Error(mensagem);
}
🔹 Generics¶
Generics permitem criar funções, hooks ou componentes que funcionam com vários tipos, mantendo a tipagem forte.
1. Função genérica¶
function identidade<T>(valor: T): T {
return valor;
}
let numero = identidade<number>(10);
let texto = identidade<string>("Oi");
2. Array genérico¶
function primeiroElemento<T>(arr: T[]): T | undefined {
return arr[0];
}
let primeiro = primeiroElemento([1, 2, 3]); // 1
let palavra = primeiroElemento(["a", "b", "c"]); // "a"
3. Restrição de tipo (extends)¶
function obterTamanho<T extends { length: number }>(obj: T): number {
return obj.length;
}
obterTamanho("Hello"); // 5
obterTamanho([1, 2, 3]); // 3
4. Generics em interfaces¶
interface RespostaApi<T> {
dados: T;
sucesso: boolean;
}
let respostaUsuario: RespostaApi<{ nome: string; idade: number }> = {
dados: { nome: "Ana", idade: 25 },
sucesso: true,
};
5. Generics em classes¶
class Caixa<T> {
private valor: T;
constructor(valor: T) {
this.valor = valor;
}
getValor(): T {
return this.valor;
}
}
let caixaNumero = new Caixa<number>(100);
let caixaTexto = new Caixa<string>("Genérico");
Hooks com TypeScript¶
Hooks são funções especiais do React que permitem usar o estado e outros recursos em componentes funcionais sem a necessidade de criar classes específicas. O Typescript permite a tipagem de estados, funções e contextos nos hooks.
🔹 useState¶
O useState precisa ser tipado quando o TypeScript não consegue inferir automaticamente o tipo.
1. Estado simples (number)¶
import { useState } from "react";
function Contador() {
const [contador, setContador] = useState<number>(0);
return (
<div>
<p>Valor: {contador}</p>
<button onClick={() => setContador(contador + 1)}>Incrementar</button>
</div>
);
}
2. Estado booleano¶
function Toggle() {
const [visivel, setVisivel] = useState<boolean>(false);
return (
<div>
<button onClick={() => setVisivel(!visivel)}>
{visivel ? "Ocultar" : "Mostrar"} Mensagem
</button>
{visivel && <p>🎉 Agora você está vendo a mensagem!</p>}
</div>
);
}
3. Estado com objeto¶
type Usuario = {
nome: string;
idade: number;
};
function Perfil() {
const [usuario, setUsuario] = useState<Usuario | null>(null);
return (
<div>
<button onClick={() => setUsuario({ nome: "Ana", idade: 25 })}>
Carregar Usuário
</button>
{usuario && <p>{usuario.nome} - {usuario.idade} anos</p>}
</div>
);
}
4. Estado com array¶
function Lista() {
const [itens, setItens] = useState<string[]>([]);
return (
<div>
<button onClick={() => setItens([...itens, "Novo item"])}>
Adicionar
</button>
<ul>
{itens.map((item, i) => <li key={i}>{item}</li>)}
</ul>
</div>
);
}
🔹 useEffect¶
O `useEffect` executa efeitos colaterais como requisições, timers e subscrições.
import { useState, useEffect } from "react";
function Relogio() {
const [hora, setHora] = useState<Date>(new Date());
useEffect(() => {
const timer = setInterval(() => setHora(new Date()), 1000);
return () => clearInterval(timer); // cleanup
}, []);
return <h2>{hora.toLocaleTimeString()}</h2>;
}
🔹 useContext¶
O useContext permite compartilhar estado global sem precisar passar props manualmente.
import { createContext, useContext, useState } from "react";
type Tema = "claro" | "escuro";
const TemaContext = createContext<{
tema: Tema;
alternar: () => void;
}>({
tema: "claro",
alternar: () => {},
});
function TemaProvider({ children }: { children: React.ReactNode }) {
const [tema, setTema] = useState<Tema>("claro");
const alternar = () => setTema(tema === "claro" ? "escuro" : "claro");
return (
<TemaContext.Provider value={{ tema, alternar }}>
{children}
</TemaContext.Provider>
);
}
function BotaoTema() {
const { tema, alternar } = useContext(TemaContext);
return <button onClick={alternar}>Tema atual: {tema}</button>;
}
function App() {
return (
<TemaProvider>
<BotaoTema />
</TemaProvider>
);
}
🔹 useReducer¶
O useReducer é útil para estados mais complexos ou com múltiplas transições.
import { useReducer } from "react";
type Estado = { contador: number };
type Acao = { type: "incrementar" } | { type: "decrementar" };
function reducer(estado: Estado, acao: Acao): Estado {
switch (acao.type) {
case "incrementar":
return { contador: estado.contador + 1 };
case "decrementar":
return { contador: estado.contador - 1 };
default:
return estado;
}
}
function ContadorReducer() {
const [estado, dispatch] = useReducer(reducer, { contador: 0 });
return (
<div>
<p>Contador: {estado.contador}</p>
<button onClick={() => dispatch({ type: "incrementar" })}>+</button>
<button onClick={() => dispatch({ type: "decrementar" })}>-</button>
</div>
);
}
🔹 Custom Hooks¶
Custom hooks permitem extrair lógica reutilizável em funções próprias. A seguir segue um exemplo de hook para armazenar estado no localStorage.
import { useState, useEffect } from "react";
function useLocalStorage<T>(chave: string, valorInicial: T) {
const [valor, setValor] = useState<T>(() => {
const salvo = localStorage.getItem(chave);
return salvo ? (JSON.parse(salvo) as T) : valorInicial;
});
useEffect(() => {
localStorage.setItem(chave, JSON.stringify(valor));
}, [chave, valor]);
return [valor, setValor] as const;
}
function App() {
const [nome, setNome] = useLocalStorage<string>("nome", "");
return (
<div>
<input value={nome} onChange={(e) => setNome(e.target.value)} />
<p>Nome salvo: {nome}</p>
</div>
);
}
Tratamento de Eventos e Tipos de JSX¶
No React, os eventos são usados para interagir com os elementos da interface, como cliques, mudanças de input, submissão de formulários, etc.
No TypeScript, podemos tipar esses eventos para garantir maior segurança e evitar erros em tempo de compilação.
🔹 Tratamento de Eventos¶
1. Tipos básicos de eventos¶
O TypeScript oferece tipos específicos para cada tipo de evento no React, disponíveis no namespace React.
Alguns exemplos:
| Evento | Tipo |
|---|---|
| Click em botão | React.MouseEvent<HTMLButtonElement> |
| Mudança em input | React.ChangeEvent<HTMLInputElement> |
| Submit de formulário | React.FormEvent<HTMLFormElement> |
| Foco/Blur | React.FocusEvent<HTMLInputElement> |
2. Exemplo: Evento de click¶
import React from "react";
function Botao() {
const handleClick = (evento: React.MouseEvent<HTMLButtonElement>) => {
console.log("Botão clicado!", evento);
};
return <button onClick={handleClick}>Clique aqui</button>;
}
3. Exemplo: Evento de input¶
import React, { useState } from "react";
function Input() {
const [valor, setValor] = useState("");
const handleChange = (evento: React.ChangeEvent<HTMLInputElement>) => {
setValor(evento.target.value);
};
return (
<div>
<input type="text" value={valor} onChange={handleChange} />
<p>Valor digitado: {valor}</p>
</div>
);
}
4. Exemplo: Submit de formulário¶
import React, { useState } from "react";
function Formulario() {
const [nome, setNome] = useState("");
const handleSubmit = (evento: React.FormEvent<HTMLFormElement>) => {
evento.preventDefault(); // evita reload da página
alert(`Nome enviado: ${nome}`);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={nome} onChange={e => setNome(e.target.value)} />
<button type="submit">Enviar</button>
</form>
);
}
🔹 Tipos de JSX¶
Em TypeScript, podemos tipar elementos JSX e funções que retornam JSX.
1. Função que retorna JSX¶
import React from "react";
function Saudacao(nome: string): JSX.Element {
return <h1>Olá, {nome}!</h1>;
}
JSX.Element é o tipo padrão para qualquer elemento JSX retornado por uma função. Também é possível usar React.ReactNode para aceitar JSX, strings, números ou arrays de elementos.
2. Props com JSX¶
type CardProps = {
titulo: string;
conteudo: React.ReactNode; // aceita JSX ou texto
};
function Card({ titulo, conteudo }: CardProps) {
return (
<div className="card">
<h2>{titulo}</h2>
<div>{conteudo}</div>
</div>
);
}
// Uso do componente
function App() {
return (
<Card
titulo="Meu Card"
conteudo={<p>Este é um conteúdo em JSX dentro do card!</p>}
/>
);
}
Documentação criada por Maria Clara (@MariaClara-Canuto)