Guia Completo das Novas Funcionalidades do TypeScript 5.4/5.5 - Evolução da Inferência de Tipos e Performance

2025.12.02

O TypeScript 5.4 e 5.5 trouxeram melhorias na inferência de tipos, aprimoramentos de performance e novos tipos utilitários. Neste artigo, explicamos essas novas funcionalidades com exemplos práticos de código.

Novas Funcionalidades do TypeScript 5.4

Tipo Utilitário NoInfer

NoInfer<T> é um novo tipo utilitário que suprime a inferência de parâmetros de tipo.

// NoInfer - Controle de inferência de tipos

// Problema: Inferência de tipo não intencional
function createState<T>(initial: T, allowed: T[]): T {
  return allowed.includes(initial) ? initial : allowed[0];
}

// Inferido como string ao invés de "red" | "blue"
const state1 = createState("red", ["red", "blue", "green"]);

// Solução com NoInfer
function createStateFixed<T>(initial: T, allowed: NoInfer<T>[]): T {
  return allowed.includes(initial) ? initial : allowed[0];
}

// Inferido corretamente como "red" | "blue" | "green"
const state2 = createStateFixed("red", ["red", "blue", "green"]);

// Exemplo prático: Event Handler
type EventMap = {
  click: { x: number; y: number };
  keypress: { key: string };
  scroll: { scrollY: number };
};

function addEventListener<K extends keyof EventMap>(
  event: K,
  handler: (data: NoInfer<EventMap[K]>) => void
): void {
  // implementação
}

// O tipo do handler não é inferido a partir de EventMap[K]
addEventListener("click", (data) => {
  // data é corretamente tipado como { x: number; y: number }
  console.log(data.x, data.y);
});

Melhoria no Narrowing Dentro de Closures

O narrowing de tipos dentro de funções e callbacks foi significativamente melhorado.

// Narrowing melhorado dentro de closures

function processData(value: string | number | null) {
  // TypeScript 5.3 e anteriores: Narrowing perdido dentro do callback
  // TypeScript 5.4: Narrowing mantido

  if (value === null) {
    return;
  }

  // Aqui value é string | number

  const handlers = {
    // TS 5.4: Narrowing mantido dentro da closure
    process: () => {
      // value é reconhecido como string | number
      if (typeof value === "string") {
        return value.toUpperCase();
      }
      return value.toFixed(2);
    },
    log: () => {
      // Narrowing mantido aqui também
      console.log(value);
    }
  };

  return handlers.process();
}

// Narrowing após condicionais
function example(arr: string[] | null) {
  if (arr === null) {
    return;
  }

  // TypeScript 5.4: arr é string[] dentro do callback do map
  arr.map((item) => {
    // Garantido que arr não é null
    console.log(arr.length); // OK
    return item.toUpperCase();
  });
}

// Exemplo mais complexo
type Result<T> = { success: true; data: T } | { success: false; error: string };

function processResult<T>(result: Result<T>) {
  if (!result.success) {
    return null;
  }

  // result.data está disponível
  const transformers = {
    // Reconhecido dentro da closure que result.success é true
    transform: () => {
      return result.data; // TS 5.4: OK
    }
  };

  return transformers.transform();
}

Suporte a Tipos para Object.groupBy

Suporte a tipos foi adicionado para Object.groupBy e Map.groupBy do ES2024.

// Object.groupBy - Compatível com ES2024

interface User {
  id: string;
  name: string;
  role: 'admin' | 'user' | 'guest';
  department: string;
}

const users: User[] = [
  { id: '1', name: 'Alice', role: 'admin', department: 'Engineering' },
  { id: '2', name: 'Bob', role: 'user', department: 'Engineering' },
  { id: '3', name: 'Charlie', role: 'user', department: 'Sales' },
  { id: '4', name: 'Diana', role: 'guest', department: 'Marketing' },
];

// Agrupar por role
const byRole = Object.groupBy(users, (user) => user.role);
// Tipo: Partial<Record<'admin' | 'user' | 'guest', User[]>>

console.log(byRole.admin); // [{ id: '1', name: 'Alice', ... }]
console.log(byRole.user);  // [{ id: '2', ... }, { id: '3', ... }]

// Agrupar por department
const byDepartment = Object.groupBy(users, (user) => user.department);
// Tipo: Partial<Record<string, User[]>>

// Map.groupBy - Retorna um Map
const byRoleMap = Map.groupBy(users, (user) => user.role);
// Tipo: Map<'admin' | 'user' | 'guest', User[]>

byRoleMap.forEach((users, role) => {
  console.log(`${role}: ${users.length} users`);
});

// Exemplo prático: Agrupar por data
interface Order {
  id: string;
  amount: number;
  date: Date;
}

function groupOrdersByMonth(orders: Order[]) {
  return Object.groupBy(orders, (order) => {
    const month = order.date.toISOString().slice(0, 7); // "2025-12"
    return month;
  });
}

Novas Funcionalidades do TypeScript 5.5

Type Predicates Inferidos

Os type guards agora são automaticamente inferidos a partir do valor de retorno da função.

// Type Predicates Inferidos

// TS 5.4 e anteriores: Type predicate explícito necessário
function isStringOld(value: unknown): value is string {
  return typeof value === 'string';
}

// TS 5.5: Type predicate inferido automaticamente
function isString(value: unknown) {
  return typeof value === 'string';
}
// Tipo inferido: (value: unknown) => value is string

// Uso com filter de arrays
const mixed: (string | number | null)[] = ['a', 1, null, 'b', 2];

// TS 5.4 e anteriores: Resultado do filter permanece (string | number | null)[]
const stringsOld = mixed.filter((x): x is string => typeof x === 'string');

// TS 5.5: Automaticamente inferido como string[]
const strings = mixed.filter((x) => typeof x === 'string');
// Tipo: string[]

// Remoção de null também automática
const notNull = mixed.filter((x) => x !== null);
// Tipo: (string | number)[]

// Exemplo complexo
interface User {
  id: string;
  name: string;
  email?: string;
}

const users: (User | null)[] = [
  { id: '1', name: 'Alice', email: 'alice@example.com' },
  null,
  { id: '2', name: 'Bob' },
];

// Tipo automaticamente refinado
const validUsers = users.filter((user) => user !== null && user.email);
// Tipo: User[] (apenas usuários com email)

Verificação de Sintaxe de Expressões Regulares

Verificação de sintaxe foi adicionada para literais de expressões regulares.

// Verificação de sintaxe de expressões regulares

// TS 5.5: Expressões regulares inválidas geram erro de compilação

// Erro: Sequência de escape inválida
// const invalid1 = /\p/;

// Erro: Classe de caracteres incompleta
// const invalid2 = /[a-/;

// Erro: Quantificador inválido
// const invalid3 = /a{}/;

// Expressões regulares válidas
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const phone = /^\d{3}-\d{4}-\d{4}$/;
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

// Escape de propriedades Unicode (requer flag u)
const japanese = /\p{Script=Hiragana}+/u;
const emoji = /\p{Emoji}/u;

// Grupos de captura nomeados
const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = '2025-12-02'.match(datePattern);
if (match?.groups) {
  const { year, month, day } = match.groups;
  console.log(year, month, day); // "2025", "01", "02"
}

Declarações Isoladas (Isolated Declarations)

A opção isolatedDeclarations permite acelerar a geração de declarações de tipo.

// tsconfig.json
{
  "compilerOptions": {
    "isolatedDeclarations": true,
    "declaration": true
  }
}

// Com isolatedDeclarations: true,
// é necessário explicitar tipos de retorno de funções e tipos de variáveis

// ❌ Erro: Tipo de retorno necessário
// export function add(a: number, b: number) {
//   return a + b;
// }

// ✅ OK: Tipo de retorno explícito
export function add(a: number, b: number): number {
  return a + b;
}

// ❌ Erro: Tipo da variável necessário
// export const config = { port: 3000, host: 'localhost' };

// ✅ OK: Tipo explícito
export const config: { port: number; host: string } = {
  port: 3000,
  host: 'localhost'
};

// Benefícios:
// - Possibilita paralelização do build
// - Gera .d.ts sem verificação de tipos
// - Build rápido em monorepos

Suporte a Tipos para Novos Métodos de Set

Suporte a tipos foi adicionado para métodos de Set do ES2024.

// Novos métodos de Set

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

// union: União
const union = setA.union(setB);
console.log([...union]); // [1, 2, 3, 4, 5, 6]

// intersection: Interseção
const intersection = setA.intersection(setB);
console.log([...intersection]); // [3, 4]

// difference: Diferença
const difference = setA.difference(setB);
console.log([...difference]); // [1, 2]

// symmetricDifference: Diferença Simétrica
const symmetricDiff = setA.symmetricDifference(setB);
console.log([...symmetricDiff]); // [1, 2, 5, 6]

// isSubsetOf: Verificação de subconjunto
const subset = new Set([2, 3]);
console.log(subset.isSubsetOf(setA)); // true

// isSupersetOf: Verificação de superconjunto
console.log(setA.isSupersetOf(subset)); // true

// isDisjointFrom: Verificação de disjunção
const setC = new Set([7, 8, 9]);
console.log(setA.isDisjointFrom(setC)); // true

// Exemplo prático: Filtragem por tags
interface Article {
  id: string;
  title: string;
  tags: Set<string>;
}

function filterByTags(articles: Article[], requiredTags: Set<string>): Article[] {
  return articles.filter(article =>
    requiredTags.isSubsetOf(article.tags)
  );
}

Melhorias de Performance

Aceleração da Verificação de Tipos

// Melhorias de performance do TypeScript 5.4/5.5

// 1. Verificação de tipos monomorfizada
// - Processamento acelerado para objetos com a mesma forma

// 2. Otimização de tipos condicionais
type IsString<T> = T extends string ? true : false;
// Cache interno melhorado

// 3. Otimização de build com referências de projeto
// tsconfig.json
{
  "compilerOptions": {
    "incremental": true,
    "composite": true,
    // Adicionado no 5.5
    "isolatedDeclarations": true
  },
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" }
  ]
}

// 4. Melhoria na responsividade do editor
// - Sugestões de auto-import aceleradas
// - Operações de refatoração melhoradas

Padrões de Tipos Práticos

Utilitários de Tipos Melhorados

// Padrões de tipos práticos

// 1. DeepPartial mais seguro
type DeepPartial<T> = T extends object
  ? { [P in keyof T]?: DeepPartial<T[P]> }
  : T;

interface Config {
  server: {
    port: number;
    host: string;
    ssl: {
      enabled: boolean;
      cert: string;
    };
  };
  database: {
    url: string;
    pool: number;
  };
}

// Atualização parcial de configuração
function updateConfig(partial: DeepPartial<Config>): Config {
  // Lógica de merge
  return {} as Config;
}

updateConfig({
  server: {
    ssl: {
      enabled: true
    }
  }
});

// 2. Sistema de eventos type-safe
type EventMap = {
  'user:login': { userId: string; timestamp: Date };
  'user:logout': { userId: string };
  'order:created': { orderId: string; total: number };
};

class TypedEventEmitter<T extends Record<string, unknown>> {
  private handlers = new Map<keyof T, Set<(data: any) => void>>();

  on<K extends keyof T>(event: K, handler: (data: T[K]) => void): () => void {
    if (!this.handlers.has(event)) {
      this.handlers.set(event, new Set());
    }
    this.handlers.get(event)!.add(handler);

    return () => this.handlers.get(event)?.delete(handler);
  }

  emit<K extends keyof T>(event: K, data: T[K]): void {
    this.handlers.get(event)?.forEach(handler => handler(data));
  }
}

const emitter = new TypedEventEmitter<EventMap>();

// Tratamento de eventos type-safe
emitter.on('user:login', (data) => {
  // data é { userId: string; timestamp: Date }
  console.log(data.userId, data.timestamp);
});

// 3. Propriedades condicionalmente obrigatórias
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
  Pick<T, Exclude<keyof T, Keys>> &
  { [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];

interface SearchParams {
  query?: string;
  categoryId?: string;
  tags?: string[];
}

// Pelo menos uma condição de busca é obrigatória
type ValidSearchParams = RequireAtLeastOne<SearchParams, 'query' | 'categoryId' | 'tags'>;

function search(params: ValidSearchParams) {
  // implementação
}

// OK
search({ query: 'test' });
search({ categoryId: '123' });
search({ query: 'test', tags: ['a', 'b'] });

// Erro
// search({}); // Pelo menos uma condição necessária

Guia de Migração

Migração de 5.3 para 5.4/5.5

# Upgrade
npm install typescript@latest

# Verificação de tipos
npx tsc --noEmit

# Correções comuns
// 1. Adaptação ao novo comportamento de narrowing
// Alguns códigos terão tipos inferidos corretamente,
// e asserções de tipo que eram necessárias antes podem não ser mais

// Antes (TS 5.3)
function example(value: string | null) {
  if (value === null) return;

  const fn = () => {
    // value as string era necessário
    return (value as string).toUpperCase();
  };
}

// Depois (TS 5.4+)
function exampleNew(value: string | null) {
  if (value === null) return;

  const fn = () => {
    // Asserção de tipo desnecessária
    return value.toUpperCase();
  };
}

// 2. Uso de Object.groupBy
// Verificação da configuração lib necessária
// tsconfig.json
{
  "compilerOptions": {
    "lib": ["ES2024"] // ou "ESNext"
  }
}

Resumo

O TypeScript 5.4/5.5 trouxe melhorias significativas na inferência de tipos e performance.

Principais Novas Funcionalidades

FuncionalidadeVersãoImpacto
NoInfer5.4Controle de inferência de tipos
Narrowing em closures5.4Melhoria na qualidade do código
Object.groupBy5.4Compatibilidade ES2024
Type predicates inferidos5.5Melhoria no filter etc.
isolatedDeclarations5.5Aceleração do build
Verificação de regex5.5Detecção de erros aprimorada

Recomendação de Upgrade

  • Novos projetos: Adote a versão mais recente
  • Projetos existentes: Migre gradualmente
  • Projetos grandes: Utilize isolatedDeclarations

Com a evolução do TypeScript, é possível escrever código mais seguro e eficiente.

← Voltar para a lista