JB
_
·4 min de leitura

TypeScript Avançado: Types que Vão Mudar Sua Forma de Programar

Mergulhe em Mapped Types, Template Literal Types, Conditional Types e outras features avançadas do TypeScript que tornam seu código mais seguro e expressivo.

TypeScriptJavaScriptBoas Práticas

Por que ir além do básico?

string, number, boolean... todo mundo conhece. Mas o sistema de tipos do TypeScript é muito mais poderoso. Quando você domina as features avançadas, erros que antes só apareciam em produção passam a ser capturados em tempo de compilação.

Mapped Types

Mapped Types permitem transformar cada propriedade de um tipo existente:

type User = {
  name: string;
  email: string;
  age: number;
};
 
// Torna todas as propriedades opcionais
type Partial<T> = {
  [K in keyof T]?: T[K];
};
 
// Torna todas as propriedades readonly
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};
 
// Exemplo prático: formulário de atualização
type UpdateUserDTO = Partial<User>; // todas opcionais

Indo além — criar variantes com modificadores:

// Remove opcional de todas as propriedades
type Required<T> = {
  [K in keyof T]-?: T[K];
};
 
// Remove readonly de todas as propriedades
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

Template Literal Types

Combinam string literals para criar novos tipos dinamicamente:

type EventName = 'click' | 'focus' | 'blur';
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"
 
type CSSProperty = 'margin' | 'padding';
type CSSDirection = 'Top' | 'Right' | 'Bottom' | 'Left';
type CSSShorthand = `${CSSProperty}${CSSDirection}`;
// "marginTop" | "marginRight" | ... | "paddingLeft"

Caso de uso real — tipando rotas de API:

type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type APIVersion = 'v1' | 'v2';
type Resource = 'users' | 'posts' | 'comments';
 
type Endpoint = `/${APIVersion}/${Resource}`;
// "/v1/users" | "/v1/posts" | "/v2/users" | ...
 
function request(method: HTTPMethod, endpoint: Endpoint) {
  return fetch(endpoint, { method });
}
 
request('GET', '/v1/users');    // ✅
request('GET', '/v3/users');    // ❌ Erro de compilação!

Conditional Types

Tipos que se comportam como expressões ternárias:

type IsArray<T> = T extends any[] ? true : false;
 
type A = IsArray<string[]>;  // true
type B = IsArray<string>;    // false

O poder real está com infer — extrair tipos internos:

// Extrair o tipo do elemento de um array
type ElementType<T> = T extends (infer E)[] ? E : never;
 
type Numbers = ElementType<number[]>;   // number
type Strings = ElementType<string[]>;   // string
 
// Extrair o tipo de retorno de uma função
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
 
async function fetchUser(): Promise<{ id: string; name: string }> {
  // ...
}
 
type User = Awaited<ReturnType<typeof fetchUser>>;
// { id: string; name: string }

Discriminated Unions

O pattern mais poderoso para modelar estados:

type LoadingState = { status: 'loading' };
type SuccessState<T> = { status: 'success'; data: T };
type ErrorState = { status: 'error'; message: string };
 
type AsyncState<T> = LoadingState | SuccessState<T> | ErrorState;
 
function renderUser(state: AsyncState<User>) {
  switch (state.status) {
    case 'loading':
      return <Spinner />;
    case 'success':
      return <div>{state.data.name}</div>; // TypeScript sabe que data existe
    case 'error':
      return <div>{state.message}</div>;   // TypeScript sabe que message existe
  }
}

Utility Types Menos Conhecidos

// Extract — filtra tipos de uma union
type Animals = 'cat' | 'dog' | 'fish' | 'bird';
type Mammals = Extract<Animals, 'cat' | 'dog' | 'whale'>;
// "cat" | "dog"
 
// Exclude — remove tipos de uma union
type NonFish = Exclude<Animals, 'fish'>;
// "cat" | "dog" | "bird"
 
// Parameters — extrai os parâmetros de uma função
function createUser(name: string, age: number, role: 'admin' | 'user') {}
type CreateUserParams = Parameters<typeof createUser>;
// [name: string, age: number, role: "admin" | "user"]

Um Type Guard Útil

function isError(value: unknown): value is Error {
  return value instanceof Error;
}
 
function isNonNull<T>(value: T | null | undefined): value is T {
  return value !== null && value !== undefined;
}
 
// Filtrando nulls de um array
const maybeUsers: (User | null)[] = [...];
const users: User[] = maybeUsers.filter(isNonNull);
// TypeScript infere corretamente que users é User[]

Conclusão

O sistema de tipos do TypeScript é uma linguagem dentro de uma linguagem. Investir em aprendê-lo bem significa menos bugs em produção, refatorações mais seguras e uma experiência de desenvolvimento muito mais agradável.

Comece com Mapped Types e Discriminated Unions — eles sozinhos já resolvem 80% dos casos de uso avançados que você vai encontrar no dia a dia.