Voltar para o Blog
Anúncio

TypeScript-First: Por que Todo Projeto Node.js Começa com TypeScript em 2025

Olá HaWkers, se você ainda está criando projetos Node.js novos com JavaScript puro em 2025, preciso te contar: você está nadando contra a maré. TypeScript-first tornou-se o padrão absoluto da indústria, e com razão.

A pergunta não é mais "devo usar TypeScript?" mas sim "por que eu não usaria TypeScript?". Vamos explorar essa mudança fundamental que está definindo o desenvolvimento moderno.

A Revolução TypeScript-First em Node.js

Em 2025, a maioria esmagadora dos novos projetos Node.js começa com TypeScript. Isso não é apenas uma tendência passageira - é uma mudança fundamental na forma como desenvolvemos aplicações backend.

Os Números Não Mentem

// Estatísticas de adoção em 2025
const typescriptAdoption = {
  newProjects: '87%', // Projetos novos usando TS
  migrations: '45%',  // Projetos JS sendo migrados
  jobRequirements: '92%', // Vagas exigindo TS
  npmPackages: '76%' // Top packages com tipos
};

// Principais frameworks já são TypeScript-first
const frameworkSupport = {
  nestjs: 'TypeScript nativo',
  fastify: 'Tipos first-class',
  trpc: 'TypeScript-only',
  prisma: 'TypeScript-first ORM',
  nextjs: 'TypeScript recomendado'
};
Anúncio

Por Que TypeScript Venceu?

1. Suporte Nativo do Node.js

Node.js agora tem suporte experimental para executar TypeScript diretamente:

// Execute TypeScript diretamente sem compilação!
// node --experimental-strip-types server.ts

import express from 'express';
import type { Request, Response } from 'express';

const app = express();

interface User {
  id: string;
  name: string;
  email: string;
}

app.get('/api/users/:id', async (req: Request, res: Response) => {
  const userId = req.params.id;

  const user: User = await db.users.findUnique({
    where: { id: userId }
  });

  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }

  res.json(user);
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

2. Prevenção de Bugs em Tempo de Desenvolvimento

TypeScript pega erros que só apareceriam em produção com JavaScript:

// Exemplo real: API de pagamento
interface PaymentRequest {
  amount: number;
  currency: 'USD' | 'EUR' | 'BRL';
  customerId: string;
  metadata?: Record<string, string>;
}

class PaymentService {
  async processPayment(request: PaymentRequest) {
    // TypeScript força você a lidar com todos os casos
    if (request.amount <= 0) {
      throw new Error('Amount must be positive');
    }

    // Autocomplete te mostra apenas moedas válidas
    const exchangeRate = this.getExchangeRate(request.currency);

    return {
      transactionId: generateId(),
      amount: request.amount,
      currency: request.currency,
      status: 'processed' as const
    };
  }

  private getExchangeRate(currency: PaymentRequest['currency']): number {
    // Erro de compilação se tentar passar moeda inválida
    const rates = {
      USD: 1.0,
      EUR: 1.1,
      BRL: 0.2
    };

    return rates[currency];
  }
}

// Uso - TypeScript valida em tempo de desenvolvimento
const service = new PaymentService();

// ✅ Correto
await service.processPayment({
  amount: 100,
  currency: 'USD',
  customerId: 'cus_123'
});

// ❌ Erro de compilação: "JPY" não é moeda válida
await service.processPayment({
  amount: 100,
  currency: 'JPY', // TypeScript impede isso!
  customerId: 'cus_123'
});

3. Refatoração Segura e Confiável

// Mudanças seguras em código grande
interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
  // Adiciona novo campo obrigatório
  sku: string;
}

// TypeScript imediatamente mostra todos os lugares que
// precisam ser atualizados para incluir SKU
function createProduct(data: Omit<Product, 'id'>): Product {
  return {
    id: generateId(),
    ...data
  };
}

// Erro de compilação se não passar SKU
const product = createProduct({
  name: 'Laptop',
  price: 999,
  category: 'Electronics'
  // Missing 'sku' - TypeScript não deixa passar!
});
Anúncio

Setup Moderno de TypeScript em 2025

Configuração Otimizada

// tsconfig.json - Configuração moderna e otimizada
{
  "compilerOptions": {
    "target": "ES2023",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2023"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Package.json Moderno

{
  "name": "modern-typescript-api",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/server.ts",
    "build": "tsc",
    "start": "node dist/server.js",
    "test": "vitest",
    "lint": "eslint src --ext .ts",
    "format": "prettier --write \"src/**/*.ts\""
  },
  "dependencies": {
    "fastify": "^4.25.0",
    "@fastify/cors": "^8.5.0",
    "zod": "^3.22.0"
  },
  "devDependencies": {
    "@types/node": "^20.10.0",
    "tsx": "^4.7.0",
    "typescript": "^5.3.0",
    "vitest": "^1.0.0",
    "eslint": "^8.56.0",
    "@typescript-eslint/parser": "^6.17.0",
    "@typescript-eslint/eslint-plugin": "^6.17.0",
    "prettier": "^3.1.0"
  }
}

API Moderna com Fastify + TypeScript

// src/server.ts
import Fastify from 'fastify';
import cors from '@fastify/cors';
import { z } from 'zod';

const server = Fastify({
  logger: true
});

await server.register(cors);

// Schema validation com Zod
const CreateUserSchema = z.object({
  name: z.string().min(3),
  email: z.string().email(),
  age: z.number().int().positive()
});

type CreateUserInput = z.infer<typeof CreateUserSchema>;

// Route handlers tipados
server.post<{ Body: CreateUserInput }>(
  '/users',
  async (request, reply) => {
    // Valida e parseia automaticamente
    const userData = CreateUserSchema.parse(request.body);

    const user = await db.users.create({
      data: userData
    });

    return reply.code(201).send(user);
  }
);

server.get('/users/:id', async (request, reply) => {
  const { id } = request.params as { id: string };

  const user = await db.users.findUnique({
    where: { id }
  });

  if (!user) {
    return reply.code(404).send({ error: 'User not found' });
  }

  return user;
});

const start = async () => {
  try {
    await server.listen({ port: 3000 });
    console.log('Server listening on http://localhost:3000');
  } catch (err) {
    server.log.error(err);
    process.exit(1);
  }
};

start();
Anúncio

TypeScript com Prisma: Type Safety End-to-End

// prisma/schema.prisma
model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String
  posts     Post[]
  createdAt DateTime @default(now())
}

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  String
  createdAt DateTime @default(now())
}

// src/repositories/user.repository.ts
import { PrismaClient, Prisma } from '@prisma/client';

const prisma = new PrismaClient();

export class UserRepository {
  // Prisma gera tipos automaticamente!
  async findById(id: string) {
    return prisma.user.findUnique({
      where: { id },
      include: {
        posts: {
          where: { published: true }
        }
      }
    });
  }

  async create(data: Prisma.UserCreateInput) {
    return prisma.user.create({
      data,
      include: { posts: true }
    });
  }

  async findWithPosts(options: {
    skip?: number;
    take?: number;
  }) {
    return prisma.user.findMany({
      ...options,
      include: {
        posts: {
          orderBy: { createdAt: 'desc' }
        }
      }
    });
  }
}

// Types inferidos automaticamente pelo Prisma!
const repo = new UserRepository();
const user = await repo.findById('123');
// user tem tipo completo: User & { posts: Post[] }

Testes Tipados com Vitest

// src/services/user.service.test.ts
import { describe, it, expect, beforeEach } from 'vitest';
import { UserService } from './user.service';

describe('UserService', () => {
  let service: UserService;

  beforeEach(() => {
    service = new UserService();
  });

  it('should create user with valid data', async () => {
    const userData = {
      name: 'John Doe',
      email: 'john@example.com',
      age: 30
    };

    const user = await service.createUser(userData);

    expect(user).toBeDefined();
    expect(user.id).toBeTruthy();
    expect(user.email).toBe(userData.email);
  });

  it('should throw error for invalid email', async () => {
    const userData = {
      name: 'John Doe',
      email: 'invalid-email',
      age: 30
    };

    await expect(
      service.createUser(userData)
    ).rejects.toThrow('Invalid email');
  });

  it('should find user by id', async () => {
    const created = await service.createUser({
      name: 'Jane Doe',
      email: 'jane@example.com',
      age: 25
    });

    const found = await service.findById(created.id);

    expect(found).toEqual(created);
  });
});
Anúncio

Vantagens Competitivas no Mercado

1. Código Mais Seguro e Manutenível

// Mudanças propagam automaticamente
type UserRole = 'admin' | 'user' | 'moderator';

function getUserPermissions(role: UserRole) {
  // Se adicionar nova role, TypeScript força você a
  // atualizar todos os lugares que usam roles
  switch (role) {
    case 'admin':
      return ['read', 'write', 'delete', 'admin'];
    case 'moderator':
      return ['read', 'write', 'moderate'];
    case 'user':
      return ['read'];
    // TypeScript garante que todos os casos estão cobertos
  }
}

2. Melhor Experiência de Desenvolvimento

  • Autocomplete inteligente - IDE sabe exatamente o que está disponível
  • Refatoração segura - Rename functions/variables sem medo
  • Documentação inline - Tipos são documentação viva
  • Detecção de erros instantânea - Bugs pegos em segundos, não em produção

3. Colaboração em Time

// Outros desenvolvedores entendem seu código instantaneamente
interface CreateOrderDTO {
  userId: string;
  items: Array<{
    productId: string;
    quantity: number;
    price: number;
  }>;
  shippingAddress: {
    street: string;
    city: string;
    state: string;
    zipCode: string;
    country: string;
  };
  paymentMethod: 'credit_card' | 'debit_card' | 'pix';
}

// Não precisa ler documentação - tipos explicam tudo
function createOrder(data: CreateOrderDTO): Promise<Order> {
  // Implementação...
}

Desafios e Como Superá-los

1. Curva de Aprendizado

Solução: Comece simples, adicione complexidade gradualmente

// Nível 1: Tipos básicos
function add(a: number, b: number): number {
  return a + b;
}

// Nível 2: Interfaces
interface User {
  id: string;
  name: string;
}

// Nível 3: Generics
function findById<T>(items: T[], id: string): T | undefined {
  return items.find(item => (item as any).id === id);
}

// Nível 4: Advanced types
type Nullable<T> = T | null;
type ReadOnly<T> = { readonly [K in keyof T]: T[K] };

2. Tempo de Compilação

Solução: Use ferramentas modernas como tsx para desenvolvimento rápido

# Desenvolvimento instantâneo
tsx watch src/server.ts

# Build otimizado para produção
tsc --build --incremental

O Futuro é TypeScript-First

Em 2025, TypeScript não é opcional - é esperado. Empresas procuram desenvolvedores com TypeScript, projetos open source migram para TypeScript, e novas ferramentas nascem TypeScript-first.

Se você ainda não domina TypeScript, este é o momento de investir nessa habilidade. Não é apenas sobre adicionar tipos ao JavaScript - é sobre pensar diferente, sobre construir software mais robusto e manutenível.

Para entender melhor o ecossistema moderno de Node.js, recomendo: Serverless em 2025: Como Node.js Domina a Arquitetura Sem Servidor onde exploramos como TypeScript e Node.js juntos dominam serverless.

Bora pra cima! 🦅

💻 Domine JavaScript de Verdade

TypeScript é poderoso, mas ele é construído sobre JavaScript. Dominar JavaScript profundamente é essencial para usar TypeScript efetivamente.

Preparei um material completo para você dominar JavaScript:

Formas de pagamento:

  • 3x de R$34,54 sem juros
  • ou R$97,90 à vista

📖 Ver Conteúdo Completo

Anúncio
Post anteriorPróximo post

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário