Tutorial completo de TypeScript para Web (Front-end + Back-end)

 

A seguir está um tutorial completo de TypeScript para Web (Front-end + Back-end), do zero ao avançado, com arquitetura real usada no mercado.

 


 


🚀 Tutorial Completo de TypeScript Web (Front & Back)

📌 O que é TypeScript?

TypeScript é um superset do JavaScript que adiciona:

  • Tipagem estática

  • Interfaces e enums

  • Melhor autocomplete

  • Menos bugs em produção

👉 No final, o código vira JavaScript puro.


🧱 PARTE 1 — FUNDAMENTOS DO TYPESCRIPT

1️⃣ Instalação

Requisitos:

  • Node.js (LTS)

npm install -g typescript
tsc --version

Criar projeto:

mkdir projeto-ts
cd projeto-ts
npm init -y
tsc --init

Arquivo gerado: tsconfig.json


2️⃣ Tipos básicos

let nome: string = "Vinicius";
let idade: number = 25;
let ativo: boolean = true;

let numeros: number[] = [1, 2, 3];
let dados: any = "qualquer coisa";

3️⃣ Funções tipadas

function soma(a: number, b: number): number {
  return a + b;
}

Arrow function:

const multiplicar = (a: number, b: number): number => a * b;

4️⃣ Interfaces

interface Usuario {
  id: number;
  nome: string;
  email?: string; // opcional
}

const user: Usuario = {
  id: 1,
  nome: "João"
};

5️⃣ Enums

enum Status {
  ATIVO,
  INATIVO,
  BLOQUEADO
}

let statusUser: Status = Status.ATIVO;

6️⃣ Classes

class Produto {
  constructor(
    public nome: string,
    public preco: number
  ) {}

  desconto(percentual: number): number {
    return this.preco - (this.preco * percentual / 100);
  }
}

🌐 PARTE 2 — FRONT-END COM TYPESCRIPT

Vamos usar TypeScript + Vite + React (padrão moderno).

7️⃣ Criar Front-end com TypeScript

npm create vite@latest frontend -- --template react-ts
cd frontend
npm install
npm run dev

8️⃣ Componente React em TypeScript

type Props = {
  titulo: string;
};

function Header({ titulo }: Props) {
  return <h1>{titulo}</h1>;
}

export default Header;

9️⃣ Estado tipado

import { useState } from "react";

type Usuario = {
  nome: string;
  idade: number;
};

function App() {
  const [user, setUser] = useState<Usuario>({
    nome: "Maria",
    idade: 30
  });

  return <p>{user.nome}</p>;
}

🔗 10️⃣ Consumo de API (Front)

type Produto = {
  id: number;
  nome: string;
  preco: number;
};

async function listarProdutos(): Promise<Produto[]> {
  const res = await fetch("http://localhost:3000/produtos");
  return res.json();
}

🖥️ PARTE 3 — BACK-END COM TYPESCRIPT

Vamos usar Node.js + Express + TypeScript.


11️⃣ Criar Back-end

mkdir backend
cd backend
npm init -y
npm install express cors
npm install -D typescript ts-node-dev @types/express @types/node
tsc --init

12️⃣ Estrutura de pastas

backend/
├── src/
│   ├── controllers/
│   ├── routes/
│   ├── models/
│   ├── server.ts
└── tsconfig.json

13️⃣ Servidor Express

// src/server.ts
import express from "express";
import cors from "cors";

const app = express();
app.use(cors());
app.use(express.json());

app.listen(3000, () => {
  console.log("Servidor rodando na porta 3000");
});

Rodar:

npx ts-node-dev src/server.ts

14️⃣ Model (tipagem)

// src/models/Produto.ts
export interface Produto {
  id: number;
  nome: string;
  preco: number;
}

15️⃣ Controller

import { Produto } from "../models/Produto";

const produtos: Produto[] = [
  { id: 1, nome: "Notebook", preco: 3500 }
];

export const listarProdutos = (req, res) => {
  res.json(produtos);
};

16️⃣ Rotas

import { Router } from "express";
import { listarProdutos } from "../controllers/ProdutoController";

const router = Router();

router.get("/produtos", listarProdutos);

export default router;

No server.ts:

import routes from "./routes";

app.use(routes);

🔐 PARTE 4 — BANCO DE DADOS + TYPESCRIPT

Exemplo com Prisma + PostgreSQL/MySQL:

npm install prisma
npx prisma init

Modelo:

model Usuario {
  id    Int     @id @default(autoincrement())
  nome  String
  email String  @unique
}

🧠 PARTE 5 — BOAS PRÁTICAS

✔ Tipar tudo (evite any)
✔ Criar interfaces para APIs
✔ Separar controller / service / model
✔ Compartilhar tipos entre front e back
✔ Usar ESLint + Prettier


🧩 STACK COMPLETA FINAL

  • Front-end: React + TypeScript + Vite

  • Back-end: Node.js + Express + TypeScript

  • Banco: PostgreSQL / MySQL

  • API: REST ou GraphQL

  • Deploy: Vercel (Front) / Railway (Back)


🎯 Próximos passos

Se quiser, posso:

  • Criar um projeto completo pronto

  • Ensinar TypeScript com Next.js

  • Criar API REST + Auth JWT

  • Integrar com AdMob / AdSense

  • Mostrar deploy profissional

O Prisma é o "tradutor" definitivo entre o seu código TypeScript e o seu Banco de Dados (PostgreSQL, MySQL, SQLite, etc.).

O fluxo moderno em 2026 é: Banco de Dados → Prisma → Zod → TypeScript. Se você mudar uma coluna no banco, o Prisma avisa o TypeScript, que avisa o Zod, que avisa o seu Front-end. Nada quebra sem você saber.


1. O arquivo schema.prisma

Este é o coração do Prisma. É aqui que você define como seus dados são salvos.

Snippet de código
// schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Produto {
  id        Int      @id @default(autoincrement())
  nome      String
  preco     Float
  createdAt DateTime @default(now())
}

Após rodar npx prisma generate, o Prisma cria automaticamente os tipos TypeScript para você.


2. No Back-end: Salvando de Verdade

Agora, em vez de um array na memória, usamos o Prisma Client.

TypeScript
import { PrismaClient } from '@prisma/client';
import { ProdutoSchema } from './shared/schemas';

const prisma = new PrismaClient();

app.post('/produtos', async (req: Request, res: Response) => {
  // 1. Validamos com o Zod (segurança de entrada)
  const validacao = ProdutoSchema.safeParse(req.body);

  if (!validacao.success) {
    return res.status(400).json(validacao.error.format());
  }

  try {
    // 2. Salvamos no Banco com Prisma (segurança de persistência)
    const novoProduto = await prisma.produto.create({
      data: {
        nome: validacao.data.nome,
        preco: validacao.data.preco,
      },
    });

    res.status(201).json(novoProduto);
  } catch (error) {
    res.status(500).json({ error: "Erro ao salvar no banco" });
  }
});

3. O Combo Final: Gerando Zod a partir do Prisma

Em 2026, você não precisa nem escrever o Schema do Zod manualmente se não quiser. Existem bibliotecas como a zod-prisma-types que leem seu arquivo do Prisma e geram os Schemas do Zod automaticamente.

Por que essa stack é imbatível?

CamadaFerramentaO que ela garante?
BancoPrismaQue a tabela tem as colunas e tipos certos.
LogísticaTypeScriptQue você não chame produto.valor se o nome for produto.preco.
FronteiraZodQue o que o usuário digitou faz sentido antes de tocar no banco.

🛠️ Como começar agora (O Checklist)

  1. Instale as dependências:

    npm install @prisma/client zod

    npm install prisma -D

  2. Inicie o Prisma:

    npx prisma init

  3. Modele seus dados no schema.prisma.

  4. Sincronize o banco:

    npx prisma migrate dev (Isso cria as tabelas reais).


Pergunta final para fechar o ciclo:

Agora que você tem o Front-end enviando, o Zod validando e o Prisma salvando no banco... você quer saber como fazer o deploy dessa aplicação completa (Front + Back) para que o mundo possa usar?

 

Vou criar um tutorial estruturado de TypeScript para desenvolvimento full-stack (front-end e back-end). TypeScript se tornou essencial para desenvolvimento web moderno, oferecendo tipagem estática que previne erros e melhora a manutenibilidade.

## 🎯 Objetivos de Aprendizado
Ao final deste tutorial, você será capaz de:
- Configurar TypeScript para ambientes front-end e back-end
- Criar APIs REST com Node.js/TypeScript
- Desenvolver interfaces React com TypeScript
- Entender tipos genéricos, interfaces e utilitários de tipo

## 📦 Configuração Inicial

### 1. Ambiente de Desenvolvimento
```bash
# Verifique as instalações necessárias
node --version  # Deve ser 16.x ou superior
npm --version   # 8.x ou superior

# Instalação global do TypeScript (opcional)
npm install -g typescript ts-node

# Criação do projeto
mkdir typescript-fullstack
cd typescript-fullstack
```

### 2. Estrutura do Projeto
```
typescript-fullstack/
├── backend/
│   ├── src/
│   │   ├── controllers/
│   │   ├── models/
│   │   ├── routes/
│   │   └── server.ts
│   ├── package.json
│   └── tsconfig.json
├── frontend/
│   ├── src/
│   │   ├── components/
│   │   ├── types/
│   │   └── App.tsx
│   ├── package.json
│   └── tsconfig.json
└── package.json (workspace root)
```

## 🔧 Configuração do TypeScript

### Backend (`backend/tsconfig.json`)
```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

### Frontend (`frontend/tsconfig.json`)
```json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["DOM", "DOM.Iterable", "ES2022"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}
```

## 🖥️ Backend com Node.js + Express

### 1. Instalação de Dependências
```bash
cd backend
npm init -y
npm install express cors dotenv
npm install -D typescript @types/node @types/express @types/cors ts-node nodemon
```

### 2. Tipos e Interfaces
```typescript
// src/types/user.types.ts
export interface IUser {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
  updatedAt: Date;
}

export type UserCreateInput = Omit<IUser, 'id' | 'createdAt' | 'updatedAt'>;
export type UserUpdateInput = Partial<UserCreateInput>;

// Exemplo de tipo genérico para respostas API
export interface ApiResponse<T = any> {
  success: boolean;
  data?: T;
  message?: string;
  error?: string;
}
```

### 3. Controlador com TypeScript
```typescript
// src/controllers/user.controller.ts
import { Request, Response } from 'express';
import { IUser, UserCreateInput, ApiResponse } from '../types/user.types';

export class UserController {
  private users: IUser[] = [];
  
  // Método tipado com generics
  public getAllUsers = async (
    req: Request,
    res: Response<ApiResponse<IUser[]>>
  ): Promise<void> => {
    try {
      res.status(200).json({
        success: true,
        data: this.users
      });
    } catch (error) {
      res.status(500).json({
        success: false,
        error: 'Internal server error'
      });
    }
  };
  
  public createUser = async (
    req: Request<{}, {}, UserCreateInput>,
    res: Response<ApiResponse<IUser>>
  ): Promise<void> => {
    try {
      const newUser: IUser = {
        id: Date.now().toString(),
        ...req.body,
        createdAt: new Date(),
        updatedAt: new Date()
      };
      
      this.users.push(newUser);
      res.status(201).json({
        success: true,
        data: newUser
      });
    } catch (error) {
      res.status(400).json({
        success: false,
        error: 'Invalid user data'
      });
    }
  };
}
```

### 4. Servidor Principal
```typescript
// src/server.ts
import express, { Application } from 'express';
import cors from 'cors';
import { UserController } from './controllers/user.controller';

class App {
  public app: Application;
  public port: number;
  private userController: UserController;
  
  constructor(port: number = 3001) {
    this.app = express();
    this.port = port;
    this.userController = new UserController();
    
    this.initializeMiddlewares();
    this.initializeRoutes();
  }
  
  private initializeMiddlewares(): void {
    this.app.use(cors());
    this.app.use(express.json());
    this.app.use(express.urlencoded({ extended: true }));
  }
  
  private initializeRoutes(): void {
    this.app.get('/api/health', (req, res) => {
      res.json({ status: 'OK', timestamp: new Date() });
    });
    
    this.app.get('/api/users', this.userController.getAllUsers);
    this.app.post('/api/users', this.userController.createUser);
  }
  
  public listen(): void {
    this.app.listen(this.port, () => {
      console.log(`🚀 Server running on http://localhost:${this.port}`);
    });
  }
}

// Inicialização
const app = new App(3001);
app.listen();
```

## 🎨 Frontend com React + TypeScript

### 1. Configuração do Projeto
```bash
cd frontend
npx create-react-app . --template typescript
npm install axios @types/axios
```

### 2. Tipos Compartilhados
```typescript
// src/types/api.types.ts
// Tipos que podem ser compartilhados com o backend
export interface IUser {
  id: string;
  name: string;
  email: string;
  createdAt: string;
  updatedAt: string;
}

export interface ApiResponse<T = any> {
  success: boolean;
  data?: T;
  message?: string;
}
```

### 3. Serviço API com TypeScript
```typescript
// src/services/api.service.ts
import axios, { AxiosInstance, AxiosResponse } from 'axios';
import { IUser, ApiResponse } from '../types/api.types';

export class ApiService {
  private api: AxiosInstance;
  
  constructor(baseURL: string = 'http://localhost:3001') {
    this.api = axios.create({
      baseURL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json'
      }
    });
  }
  
  // Método genérico para requests
  private async request<T>(
    method: string,
    url: string,
    data?: any
  ): Promise<ApiResponse<T>> {
    try {
      const response: AxiosResponse<ApiResponse<T>> = await this.api.request({
        method,
        url,
        data
      });
      return response.data;
    } catch (error) {
      return {
        success: false,
        message: 'Network error'
      };
    }
  }
  
  // Métodos específicos tipados
  public async getUsers(): Promise<ApiResponse<IUser[]>> {
    return this.request<IUser[]>('GET', '/api/users');
  }
  
  public async createUser(userData: Omit<IUser, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResponse<IUser>> {
    return this.request<IUser>('POST', '/api/users', userData);
  }
}
```

### 4. Componente React Tipado
```typescript
// src/components/UserList.tsx
import React, { useState, useEffect } from 'react';
import { ApiService } from '../services/api.service';
import { IUser } from '../types/api.types';

// Props tipadas
interface UserListProps {
  title?: string;
  maxUsers?: number;
}

const UserList: React.FC<UserListProps> = ({ 
  title = "Lista de Usuários", 
  maxUsers = 10 
}) => {
  const [users, setUsers] = useState<IUser[]>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);
  
  const apiService = new ApiService();
  
  useEffect(() => {
    fetchUsers();
  }, []);
  
  const fetchUsers = async (): Promise<void> => {
    try {
      setLoading(true);
      const response = await apiService.getUsers();
      
      if (response.success && response.data) {
        // Usando utilitários de tipo do TypeScript
        const limitedUsers = response.data.slice(0, maxUsers);
        setUsers(limitedUsers);
      } else {
        setError(response.message || 'Failed to fetch users');
      }
    } catch (err) {
      setError('Network error');
    } finally {
      setLoading(false);
    }
  };
  
  if (loading) return <div>Carregando...</div>;
  if (error) return <div>Erro: {error}</div>;
  
  return (
    <div>
      <h2>{title}</h2>
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <strong>{user.name}</strong> - {user.email}
            <br />
            <small>Criado em: {new Date(user.createdAt).toLocaleDateString()}</small>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default UserList;
```

### 5. Hook Customizado com TypeScript
```typescript
// src/hooks/useApi.ts
import { useState, useCallback } from 'react';

interface UseApiState<T> {
  data: T | null;
  loading: boolean;
  error: string | null;
}

interface UseApiResult<T> extends UseApiState<T> {
  fetchData: (url: string, options?: RequestInit) => Promise<void>;
  reset: () => void;
}

export function useApi<T = any>(initialData: T | null = null): UseApiResult<T> {
  const [state, setState] = useState<UseApiState<T>>({
    data: initialData,
    loading: false,
    error: null
  });
  
  const fetchData = useCallback(async (url: string, options?: RequestInit) => {
    setState(prev => ({ ...prev, loading: true, error: null }));
    
    try {
      const response = await fetch(url, options);
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const data: T = await response.json();
      setState({ data, loading: false, error: null });
    } catch (error) {
      setState({
        data: null,
        loading: false,
        error: error instanceof Error ? error.message : 'Unknown error'
      });
    }
  }, []);
  
  const reset = useCallback(() => {
    setState({
      data: initialData,
      loading: false,
      error: null
    });
  }, [initialData]);
  
  return { ...state, fetchData, reset };
}
```

## 🚀 Executando o Projeto Completo

### 1. Backend
```bash
cd backend
npm run dev
```
**Script no package.json:**
```json
{
  "scripts": {
    "dev": "nodemon --exec ts-node src/server.ts",
    "build": "tsc",
    "start": "node dist/server.js"
  }
}
```

### 2. Frontend
```bash
cd frontend
npm start
```

## 💡 Próximos Passos

1. **Banco de Dados**: Integre Prisma ou TypeORM com TypeScript
2. **Autenticação**: Implemente JWT com tipos seguros
3. **Testes**: Use Jest com TypeScript
4. **Deploy**: Configure Docker para ambiente de produção

TypeScript oferece segurança de tipos que previne muitos erros em tempo de desenvolvimento. A prática constante com os tipos genéricos, utilitários (`Partial`, `Pick`, `Omit`) e decorators irá solidificar seu conhecimento.