Back

5 Tipos Utilitários do TypeScript Que Você Deveria Conhecer

5 Tipos Utilitários do TypeScript Que Você Deveria Conhecer

O sistema de tipos do TypeScript se torna verdadeiramente poderoso quando você descobre os tipos utilitários—auxiliares integrados que transformam tipos existentes sem reescrevê-los. Se você já se viu duplicando interfaces ou lutando com modificações de tipos, estes cinco tipos utilitários essenciais vão mudar como você escreve código TypeScript.

Pontos-Chave

  • Tipos utilitários transformam tipos existentes sem reescrevê-los, reduzindo duplicação de código
  • Partial torna todas as propriedades opcionais, perfeito para operações de atualização
  • Required força todas as propriedades, garantindo estruturas de dados completas
  • Pick e Omit ajudam a criar interfaces focalizadas selecionando ou excluindo propriedades
  • Readonly previne mutações acidentais, essencial para padrões de dados imutáveis

O Que São Tipos Utilitários do TypeScript e Por Que Importam

Tipos utilitários são tipos genéricos pré-construídos do TypeScript que constroem novos tipos transformando os existentes. Em vez de criar manualmente variações das suas interfaces, estes utilitários permitem derivar novos tipos com modificações específicas—tornando seu código mais manutenível e reduzindo duplicação de tipos.

Vamos explorar cinco tipos utilitários fundamentais que resolvem problemas comuns do mundo real: Partial, Required, Pick, Omit e Readonly. Cada um aborda cenários específicos que você encontra diariamente no desenvolvimento TypeScript.

Partial<T>: Tornando Todas as Propriedades Opcionais no TypeScript

O tipo utilitário Partial transforma todas as propriedades de um tipo em opcionais, perfeito para operações de atualização onde você modifica apenas campos específicos.

Quando Usar Partial para Atualizações de Formulário

Considere uma atualização de perfil de usuário onde os usuários podem alterar campos individuais sem enviar o objeto inteiro:

interface User {
  id: string;
  name: string;
  email: string;
  avatar: string;
  lastLogin: Date;
}

function updateUserProfile(userId: string, updates: Partial<User>) {
  // Atualiza apenas os campos fornecidos
  return api.patch(`/users/${userId}`, updates);
}

// Uso - atualizando apenas o email
updateUserProfile("123", { email: "new@email.com" });

Exemplo de Tipo Partial com Código do Mundo Real

Atualizações de estado do React se beneficiam significativamente do Partial:

const [user, setUser] = useState<User>(initialUser);

const updateUser = (updates: Partial<User>) => {
  setUser(prev => ({ ...prev, ...updates }));
};

// Atualiza apenas campos específicos
updateUser({ name: "Jane Doe", avatar: "new-avatar.jpg" });

Required<T>: Forçando Todas as Propriedades no TypeScript

Required faz o oposto do Partial—torna todas as propriedades opcionais obrigatórias, garantindo estruturas de dados completas.

Convertendo Propriedades Opcionais em Obrigatórias

Isso se mostra inestimável para validação de configuração:

interface DatabaseConfig {
  host?: string;
  port?: number;
  username?: string;
  password?: string;
}

// Garante que todos os valores de configuração existam antes de conectar
type ValidatedConfig = Required<DatabaseConfig>;

function connectDatabase(config: ValidatedConfig) {
  // Todas as propriedades garantidamente existem
  return createConnection(config);
}

Tipo Required em Ação

Submissão de formulários frequentemente requer dados completos:

interface RegistrationForm {
  username?: string;
  email?: string;
  password?: string;
  terms?: boolean;
}

type CompleteRegistration = Required<RegistrationForm>;

function submitRegistration(data: CompleteRegistration) {
  // Todos os campos devem estar preenchidos
  api.post('/register', data);
}

Pick<T, K>: Selecionando Propriedades Específicas de Tipos

Pick cria um novo tipo selecionando apenas propriedades especificadas, ideal para criar interfaces focalizadas.

Criando Subconjuntos de Tipos Focalizados com Pick

Extraia apenas o que você precisa para componentes específicos:

interface Article {
  id: string;
  title: string;
  content: string;
  author: string;
  publishedAt: Date;
  tags: string[];
}

// Componente de card precisa apenas destes campos
type ArticlePreview = Pick<Article, 'id' | 'title' | 'author'>;

function ArticleCard({ article }: { article: ArticlePreview }) {
  return (
    <div>
      <h3>{article.title}</h3>
      <p>por {article.author}</p>
    </div>
  );
}

Pick vs Omit: Escolhendo a Abordagem Certa

Use Pick quando precisar de poucas propriedades, Omit quando excluir poucas propriedades:

// Pick: Quando você precisa de 2-3 propriedades de um tipo grande
type UserSummary = Pick<User, 'id' | 'name'>;

// Omit: Quando você precisa da maioria das propriedades exceto algumas
type PublicUser = Omit<User, 'password'>;

Omit<T, K>: Excluindo Propriedades de Tipos TypeScript

Omit cria um tipo excluindo propriedades especificadas, perfeito para remover campos sensíveis ou internos.

Removendo Dados Sensíveis com Omit

Proteja informações sensíveis em respostas de API:

interface UserAccount {
  id: string;
  email: string;
  password: string;
  creditCard: string;
  publicProfile: boolean;
}

// Remove campos sensíveis para API pública
type PublicUserData = Omit<UserAccount, 'password' | 'creditCard'>;

function getPublicProfile(userId: string): Promise<PublicUserData> {
  return api.get(`/users/${userId}/public`);
}

Melhores Práticas do Tipo Utilitário Omit

Combine com outros utilitários para padrões poderosos:

// Remove campos internos e torna os restantes opcionais
type UserUpdatePayload = Partial<Omit<User, 'id' | 'createdAt'>>;

Readonly<T>: Criando Tipos Imutáveis no TypeScript

Readonly torna todas as propriedades imutáveis, prevenindo mutações acidentais e garantindo integridade dos dados.

Prevenindo Mutações Acidentais com Readonly

Essencial para gerenciamento de estado e configuração:

interface AppConfig {
  apiEndpoint: string;
  version: string;
  features: string[];
}

type ImmutableConfig = Readonly<AppConfig>;

const config: ImmutableConfig = {
  apiEndpoint: "https://api.example.com",
  version: "1.0.0",
  features: ["auth", "payments"]
};

// Erro do TypeScript: Cannot assign to 'apiEndpoint' because it is a read-only property
config.apiEndpoint = "https://new-api.com"; // ❌

Propriedades Readonly e Props do React

Garanta que props permaneçam inalteradas:

type UserCardProps = Readonly<{
  user: User;
  onSelect: (id: string) => void;
}>;

function UserCard({ user, onSelect }: UserCardProps) {
  // Props são imutáveis dentro do componente
  return <div onClick={() => onSelect(user.id)}>{user.name}</div>;
}

Combinando Múltiplos Tipos Utilitários para Padrões Avançados

Tipos utilitários se tornam ainda mais poderosos quando combinados:

interface DatabaseRecord {
  id: string;
  createdAt: Date;
  updatedAt: Date;
  deletedAt?: Date;
  data: Record<string, unknown>;
}

// Cria um tipo para novos registros: sem timestamps, dados parciais
type NewRecord = Omit<DatabaseRecord, 'id' | 'createdAt' | 'updatedAt'> & {
  data: Partial<DatabaseRecord['data']>;
};

// Tipo de atualização: tudo opcional exceto ID
type UpdateRecord = Partial<Omit<DatabaseRecord, 'id'>> & Pick<DatabaseRecord, 'id'>;

Conclusão

Estes cinco tipos utilitários—Partial, Required, Pick, Omit e Readonly—formam a base do desenvolvimento eficaz em TypeScript. Eles eliminam definições repetitivas de tipos, garantem segurança de tipos e tornam seu código mais manutenível.

Comece incorporando estes padrões na sua base de código. Inicie com Partial para operações de atualização, use Omit para criar interfaces públicas seguras e aproveite Readonly para prevenir bugs. À medida que se torna confortável, combine-os para criar transformações sofisticadas de tipos que correspondem perfeitamente às necessidades da sua aplicação.

Perguntas Frequentes

Sim, você pode aninhar tipos utilitários para criar transformações complexas. Por exemplo, Partial e Omit funcionam bem juntos para criar payloads de atualização que excluem certos campos enquanto tornam outros opcionais.

Não, tipos utilitários são construções puramente de tempo de compilação. O TypeScript remove todas as informações de tipo durante a compilação, então tipos utilitários têm zero overhead em runtime.

Extensão de interface cria novos tipos adicionando propriedades, enquanto tipos utilitários transformam tipos existentes. Tipos utilitários oferecem mais flexibilidade para modificar tipos que você não controla ou criar variações de tipos existentes.

Sim, o TypeScript permite criar tipos utilitários personalizados usando mapped types, conditional types e template literal types. Comece com utilitários integrados e crie personalizados quando identificar padrões repetidos.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay