Extração de Tipos a partir de Funções
Em cenários de desenvolvimento real, muitas vezes precisamos reutilizar as definições de tipos de uma função já existente sem a necesssidade de rdeeclará-las manualmente. O TypeScript oferece utilitários poderosos para extrair essas informações através de inferência.
function processarRegistro(dados: { uuid: string; ativo: boolean }): { status: string; timestamp: number }[] {
// Lógica interna simplificada
return [{ status: "sucesso", timestamp: Date.now() }];
}
// Extraindo o tipo do retorno
type ResultadoProcessamento = ReturnType<typeof processarRegistro>;
/*
type ResultadoProcessamento = {
status: string;
timestamp: number;
}[]
*/
// Extraindo o tipo dos parâmetros
type ParametrosProcessamento = Parameters<typeof processarRegistro>;
/*
type ParametrosProcessamento = [dados: {
uuid: string;
ativo: boolean;
}]
*/
Tipagem em Requisições Assíncronas
Ao trabalhar com APIs, é fundamental garantir que a resposta do servidor esteja devidamente tipada para evitar erros em tempo de execução. O uso de Generics permite criar estruturas flexíveis para diferentes endpoints.
import axios from 'axios';
interface RespostaPadrao<T = any> {
data: T;
error: boolean;
meta: { version: string };
}
interface PerfilUsuario {
nickname: string;
level: number;
}
// Abordagem com Generics dinâmicos
export async function buscarPerfil<T>() {
return axios.get<RespostaPadrao<T>>('/api/profile')
.then(res => res.data)
.catch(err => console.error("Erro na requisição", err));
}
async function execucao() {
// O tipo 'usuario' conterá a estrutura de PerfilUsuario dentro de 'data'
const usuario = await buscarPerfil<PerfilUsuario>();
}
// Abordagem com tipo pré-definido na função
export function buscarPerfilEspecífico() {
return axios.get<RespostaPadrao<PerfilUsuario>>('/api/profile');
}
A Importância da Ordem em Sobrecargas de Funções
O TypeScript resolve sobrecargas de funções seguindo a ordem de declaração. Se uma definição genérica for colocada antes de uma definição específica, a versão mais específica nunca será alcançada.
// Padrão Incorreto: O tipo 'unknown' captura tudo antes dos tipos específicos
declare function manipular(input: unknown): unknown;
declare function manipular(input: HTMLInputElement): number;
const inputEl: HTMLInputElement = {} as any;
const res = manipular(inputEl); // res é inferido como 'unknown'
// Padrão Correto: Do mais específico para o mais genérico
declare function processarElemento(el: HTMLButtonElement): string;
declare function processarElemento(el: HTMLElement): number;
declare function processarElemento(el: any): any;
const botao: HTMLButtonElement = {} as any;
const resultado = processarElemento(botao); // resultado é inferido como 'string'
Combinação e Extensão de Declarações
O TypeScript permite que múltiplas declarações com o mesmo nome coexistam, mesclando suas propriedades. Isso é útil para estender bibliotecas ou organizar código complexo.
Mesclagem de Interfaces
interface AppConfig {
apiHost: string;
}
interface AppConfig {
timeout: number;
}
const config: AppConfig = {
apiHost: "https://api.exemplo.com",
timeout: 5000
};
Classes e Namespaces
É possível combinar classes com namespaces para adicionar propriedades estáticas ou tipos internos de forma organizada.
class GerenciadorVendas {}
namespace GerenciadorVendas {
export interface Detalhes {
idVenda: number;
valor: number;
}
export const versao = "1.0.0";
}
let info: GerenciadorVendas.Detalhes;
console.log(GerenciadorVendas.versao);
Checagem de Tipos em Arquivos JavaScript
Em projetos legados ou em transição para TypeScript, é possível utilizar o motor do TS para validar arquivos .js através de anotações JSDoc e da diretiva @ts-check.
// @ts-check
/** @type {string} */
let identificador;
identificador = "ID-123"; // Correto
// identificador = 456; // Erro detectado pelo TypeScript: Type 'number' is not assignable to type 'string'.
Utilização de Bibliotecas de Tipos Utilitários
Para manipulações avançadas de tipos, como mesclagem profunda ou validação de campos obrigatórios parciais, bibliotecas como o type-fest são altamente recomendadas.
import type { Merge, RequireAtLeastOne } from 'type-fest';
interface BaseInfo {
id: string;
criadoEm: Date;
}
type UpdateInfo = {
id: number; // Substituindo o tipo da base
atualizadoEm: Date;
};
// Mesclando tipos onde UpdateInfo sobrescreve propriedades conflitantes de BaseInfo
type DadosCompletos = Merge<BaseInfo, UpdateInfo>;
// Exemplo: Garantir que ao menos um meio de contato seja fornecido
type Contato = {
email?: string;
telefone?: string;
whatsapp?: string;
};
const registro: RequireAtLeastOne<Contato, 'email' | 'telefone'> = {
email: "dev@exemplo.com"
};
Desafios de Tipagem
Para desenvolvedores que desejam aprofundar seu conhecimento no sistema de tipos (Type System), existem comunidades dedicadas a desafios de lógica pura em TypeScript, como o type-challenges. Embora casos extremos de "ginástica de tipos" sejam raros no dia a dia corporativo, praticar essas técnicas melhora significativamente a capacidade de criar abstrações robustas e seguras em aplicações de grande escala.