TypeScript: Tipos cruzados e composição avançada no…

Última atualização: 2026-08-26

Os tipos de interseção utilizam & para combinar vários tipos em um único tipo — o novo tipo possui todas as características de cada um dos tipos originais. Trata-se de uma ferramenta essencial para tipos compostos no TypeScript.

1. Noções básicas sobre tipos de cruzamentos

(1) Sintaxe básica

TYPESCRIPT
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;

let person: Person = {
  name: "Charlie",
  age: 20
  // Both must be present name and age
};

(2) Comparação com tipos de união

Operador Significado Analogia
A & B (OU) Satisfaz tanto A quanto B E (e)
`A B` (Conjunção) Satisfaz A ou B
TYPESCRIPT
type StringOrNumber = string | number;   // Joint:It could be one of them
type StringAndNumber = string & number;   // Crossing:It must be both at the same time → never!

// Meaningful Crossovers——Object Type
type HasId = { id: number };
type HasName = { name: string };
type Entity = HasId & HasName;   // ✅ At the same time, there are id and name

(3) Sobreposição entre vários tipos

TYPESCRIPT
type Timestamped = { createdAt: Date; updatedAt: Date };
type SoftDeletable = { deletedAt: Date | null };
type Auditable = { createdBy: string; updatedBy: string };

type FullEntity = Timestamped & SoftDeletable & Auditable;

let article: FullEntity = {
  createdAt: new Date(),
  updatedAt: new Date(),
  deletedAt: null,
  createdBy: "admin",
  updatedBy: "editor"
};


2. Regras de fusão para dados de tipos diferentes

(1) Propriedades com o mesmo nome — determine a interseção dos tipos

Quando dois tipos possuem propriedades com o mesmo nome, mas são de tipos diferentes, o resultado da interseção é a interseção dos dois tipos:

TYPESCRIPT
type A = { value: string | number };
type B = { value: string | boolean };
type C = A & B;

// C 's value Type = (string | number) & (string | boolean) = string
let c: C = { value: "hello" };     // ✅ string It is the intersection
// let c2: C = { value: 42 };      // ❌ number Not in the intersection
// let c3: C = { value: true };    // ❌ boolean Not in the intersection

(2) Propriedades com o mesmo nome — tipos incompatíveis resultam em um erro “never”

Quando as propriedades de dois tipos com o mesmo nome não se sobrepõem, o resultado é never — é impossível que tal valor exista:

TYPESCRIPT
type A = { id: string };
type B = { id: number };
type C = A & B;

// C 's id Type = string & number = never
// No value can be both string and number
// let c: C = { id: "x" };  // ❌ You cannot string Assigned never
⚠️ Observação: O TypeScript não gera ativamente um erro quando um tipo cruzado resulta em never — ele simplesmente torna o tipo indisponível. Essa é a principal diferença entre tipos cruzados e interface extends: extends gera um erro quando um conflito é detectado, enquanto os tipos cruzados & resultam silenciosamente em never.

(3) Interseção de tipos de função

Quando os tipos de função se cruzam, os parâmetros são considerados como sua interseção (o que geralmente resulta em never):

TYPESCRIPT
type StringHandler = (value: string) => void;
type NumberHandler = (value: number) => void;
type MixedHandler = StringHandler & NumberHandler;

// MixedHandler parameters = string & number = never
// In fact, there is no value that can satisfy both signatures at the same time.
💡 Dica: A sobrecarga de funções raramente é usada no desenvolvimento normal. Se você precisar de uma função que “possa lidar com vários tipos”, use tipos de união (union types) em vez da sobrecarga.



3. A diferença entre o tipo cruzado e interface extends

(1) Comparação sintática

TYPESCRIPT
// interface extends
interface Person {
  name: string;
}
interface Employee extends Person {
  employeeId: string;
}

// type Crossing
type Person2 = { name: string };
type Employee2 = Person2 & { employeeId: string };

(2) Comparação entre métodos de resolução de conflitos

Cenário interface estende interseção de tipos &
Compatibilidade entre tipos de atributos com o mesmo nome ✅ O subtipo substitui o supertipo Considere a interseção
Tipos de propriedade incompatíveis com o mesmo nome ❌ Erro de compilação Gera silenciosamente um never
Herança múltipla Apenas herança única Herança cruzada múltipla permitida
Declaração incorporada Apoio Não apoio

(3) Recomendações de seleção

TYPESCRIPT
// Use extends scenario — Compile-time conflict detection is required
interface BaseConfig {
  host: string;
  port: number;
}

interface DevConfig extends BaseConfig {
  debug: boolean;    // ✅ New Properties
  // host: number;   // ❌ Compilation error——Parent Type host: string Conflict
}

// Using Intercut Scenes——Requires flexible combinations of multiple types
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithAudit = { createdBy: string; updatedBy: string };
type FullRecord = BaseConfig & WithTimestamps & WithAudit;
// Quick Combinations,No need to define an intermediate step interface


4. Padrões comuns para combinações de tipos

(1) Padrão 1: Mixin

"Incorporar" funcionalidades adicionais a um objeto por meio de tipos de interface:

TYPESCRIPT
type WithId = { id: number };
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithSoftDelete = { deletedAt: Date | null };
type WithAudit = { createdBy: string; updatedBy: string };

// Freely combine different abilities
type BaseEntity = WithId & WithTimestamps;
type FullEntity = WithId & WithTimestamps & WithSoftDelete & WithAudit;

interface Article extends BaseEntity {
  title: string;
  content: string;
}

let article: Article = {
  id: 1,
  createdAt: new Date(),
  updatedAt: new Date(),
  title: "TypeScript Getting Started",
  content: "TypeScript is a superset of JavaScript..."
};

(2) Modelo 2: Combinação de condições

Selecione se deseja marcar um tipo específico com base nas condições:

TYPESCRIPT
type EntityWithOptional<T, TExtra> = T & Partial<TExtra>;

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

interface UserProfile {
  avatar: string;
  bio: string;
}

// User + Optional Profile
type UserWithOptionalProfile = EntityWithOptional<User, UserProfile>;
// { id: number; name: string; avatar?: string; bio?: string }

(3) Modelo Três: Tipos de marca

Use tipos cruzados para “identificar” tipos primitivos e evitar seu uso indevido:

TYPESCRIPT
type USD = number & { __brand: "USD" };
type EUR = number & { __brand: "EUR" };

function createUSD(amount: number): USD {
  return amount as USD;
}

function createEUR(amount: number): EUR {
  return amount as EUR;
}

let price: USD = createUSD(100);
let cost: EUR = createEUR(80);

// price = cost;          // ❌ EUR Cannot be assigned to USD
// price + cost;          // ❌ Cannot be combined

function addUSD(a: USD, b: USD): USD {
  return (a + b) as USD;  // Calculations can be made within the same currency
}

let total = addUSD(price, createUSD(50));
console.log(total);  // 150

▶ Exemplo: Combinações de configuração com segurança de tipos

TYPESCRIPT
// Basic Configuration
type BaseConfig = {
  host: string;
  port: number;
};

// Additional Development Environment Configuration
type DevConfig = BaseConfig & {
  debug: true;
  mockApi: boolean;
};

// Additional Configuration for the Production Environment
type ProdConfig = BaseConfig & {
  debug: false;
  ssl: boolean;
  maxConnections: number;
};

function createDevConfig(): DevConfig {
  return { host: "localhost", port: 3000, debug: true, mockApi: true };
}

function createProdConfig(): ProdConfig {
  return { host: "api.example.com", port: 443, debug: false, ssl: true, maxConnections: 100 };
}

let dev = createDevConfig();
let prod = createProdConfig();

console.log(`Development:${dev.host}:${dev.port} (mock: ${dev.mockApi})`);
console.log(`Production:${prod.host}:${prod.port} (ssl: ${prod.ssl})`);
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Development:localhost:3000 (mock: true)
Production:api.example.com:443 (ssl: true)


5. Técnicas avançadas para operações entre diferentes tipos

(1) Conversão recursiva de tipos

TYPESCRIPT
type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object
    ? T[K] extends Array<any>
      ? T[K]
      : DeepPartial<T[K]>
    : T[K];
};

interface Config {
  server: { host: string; port: number };
  database: { url: string; pool: { min: number; max: number } };
}

type PartialConfig = DeepPartial<Config>;
let cfg: PartialConfig = {};  // All levels are available

(2) Interseções em funções genéricas

TYPESCRIPT
function merge<T extends object, U extends object>(a: T, b: U): T & U {
  return { ...a, ...b };
}

let person = merge({ name: "Charlie" }, { age: 20 });
// person Type:{ name: string } & { age: number }

(3) Interseções em tipos condicionais

TYPESCRIPT
type AddTimestamps<T> = T & { createdAt: Date; updatedAt: Date };

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

type TimestampedUser = AddTimestamps<User>;
// { name: string; email: string; createdAt: Date; updatedAt: Date }


▶ Exemplo: Tipos utilitários construídos com interseção

Saída:

TEXT 📖 Somente leitura
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)
TYPESCRIPT
type WithId = { id: number };
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithSoftDelete = { deletedAt: Date | null };

type Entity = WithId & WithTimestamps & WithSoftDelete;

type Creatable<T> = Omit<T, "id" | "createdAt" | "updatedAt" | "deletedAt">;
type Updatable<T> = Partial<Omit<T, "id">>;

interface Product extends Entity {
  name: string;
  price: number;
}

type CreateProduct = Creatable<Product>;
// { name: string; price: number }

type UpdateProduct = Updatable<Product>;
// { name?: string; price?: number; createdAt?: Date; ... }

let newProduct: CreateProduct = { name: "Widget", price: 9.99 };
let update: UpdateProduct = { price: 12.99 };
console.log(update.price);  // 12.99

Saída:

TEXT 📖 Somente leitura
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)

▶ Exemplo: Tipos marcados para segurança de domínio

Saída:

TEXT 📖 Somente leitura
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)
TYPESCRIPT
type UserId = number & { __brand: "UserId" };
type OrderId = number & { __brand: "OrderId" };

function createUserId(n: number): UserId { return n as UserId; }
function createOrderId(n: number): OrderId { return n as OrderId; }

let uid = createUserId(42);
let oid = createOrderId(99);

// uid = oid;  // ❌ OrderId is not assignable to UserId

function findUser(id: UserId): string {
  return `User #${id}`;
}

console.log(findUser(uid));        // "User #42"
// console.log(findUser(oid));     // ❌ prevents accidental misuse

Saída:

TEXT 📖 Somente leitura
Development: localhost:3000 (mock: true)
Production: api.example.com:443 (ssl: true)

❓ Perguntas Frequentes

P: Como faço para solucionar um erro “never” causado por interseções de tipos? R: Remova gradualmente os membros que se intersectam para identificar quais dois tipos estão em conflito. Uma causa comum são tipos de propriedade incompatíveis com o mesmo nome (por exemplo, string & number). Recomendamos usar interface extends em vez de interseções — extends gera um erro imediatamente quando há um conflito, facilitando a identificação do problema.

P: O cross-typing pode substituir a herança? R: Sim, na maioria dos casos, mas não são totalmente equivalentes. interface extends oferece detecção de conflitos em tempo de compilação, fusão de declarações e suporte para class implements. O cross-typing é mais flexível, mas não possui detecção de conflitos. Recomendamos usar extends para combinar tipos de objetos e o cross-typing para combinações simples e rápidas.

P: Os tipos com nome próprio são úteis no desenvolvimento prático? R: Eles são muito úteis quando é preciso distinguir entre tipos que têm a mesma estrutura, mas significados diferentes. Exemplos típicos incluem moedas (USD x EUR), IDs (UserId x OrderId) e unidades de medida (metros x pés). Os tipos marcados evitam confusões acidentais e detectam erros — como tratar euros como dólares — já na fase de compilação.

P: Os tipos disjuntivos e os tipos conjuntivos podem ser usados juntos? R: Sim. type T = (A & B) | (C & D) significa “ou A e B são satisfeitos, ou C e D são satisfeitos”. Os parênteses determinam a precedência — & tem precedência maior do que |, portanto A & B | C & D é igual a (A & B) | (C & D).

📖 Resumo

📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Defina dois tipos, WithId e WithTimestamps, combine-os usando um tipo cruzado para formar BaseEntity e crie um objeto que satisfaça BaseEntity.
  2. Problema avançado (Dificuldade ⭐⭐): Implemente os tipos de marca UserId = number & { __brand: "UserId" } e OrderId = number & { __brand: "OrderId" }. Escreva funções para criar cada tipo de ID e verifique se eles não podem ser atribuídos uns aos outros.
  3. Problema de desafio (Dificuldade ⭐⭐⭐): Implemente o tipo de ferramenta Overwrite<T, U> — substitua as propriedades em T com o mesmo nome pelas propriedades de U, preservando as propriedades com nomes diferentes. Dica: Use um tipo de mapeamento + um tipo cruzado.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%