Funções Genéricas Inteligentes
Publicado em 18 de junho de 2025 por William Gonçalves - 7 minutos de leitura
O que faz uma lib parecer "mágica"? Inferência de tipos!
E você pode usar isso hoje!
Funções reutilizáveis são o coração de qualquer projeto ou lib
Imagine que precisamos retornar nome e idade de um usuário. A solução mais rápida seria essa:
type User = {
id: string;
name: string;
age: number;
email: string;
}
function getNameAndAge(user: User): { name: string; age: number } {
return {
name: user.name,
age: user.age,
};
}
const user: User = {
id: '1234',
name: 'William',
age: 36,
email: 'iwilldev@outlook.com.br'
}
const userNameAndAge = getNameAndAge(user);
// valor: { "name": "William", "age": 36 }
// tipo: { name: string, age: number }
E qual é o problema nisso?
- ❌ A função só serve pra isso
- ❌ Você provavelmente não vai usar de novo
- ❌ Ela infere um tipo sem relação com o original
- ❌ Ou você vai declarar um novo tipo pro retorno
A mágica dos generics + inferência
Vamos criar uma função pick
que aceita como parâmetro: (a) um objeto e (b) um array de chaves correspondentes a ele. Como retorno da função, um "partial" do tipo original.
function pick<T, K extends keyof T>(
obj: T,
keys: K[]
): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) {
result[key] = obj[key];
}
return result;
}
O resultado?
Uma implementação segura, precisa, flexível e reutilizável, mantendo referência ao tipo original
const user: User = {
id: '1234',
name: 'William',
age: 36,
email: 'iwilldev@outlook.com.br'
}
const userNameAndAge = pick(user, ["name", "age"])
// valor igual 🙃: { "name": "William", "age": 36 }
// tipo relacionado ao original: Pick<User, "name" | "age">
A partir daí, você vai ao infinito e além 👉
- Uma função
omit
, para remover propriedades de um objeto
type OmitKeys<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
function omit<T, K extends keyof T>(
obj: T,
keys: K[]
): OmitKeys<T, K> {
const result = { ...obj };
keys.forEach(key => delete result[key]);
return result;
}
const userWithoutId = omit(user, ["id"])
/* valor: {
"name": "William",
"age": 36,
"email": "iwilldev@outlook.com.br"
} */
// tipo: OmitKeys<User, "id">
- Uma
merge
, para combinar dois objetos diferentes
// const user: User = { ... }
type Role = {
role: 'admin' | 'editor' | 'viewer';
permissions: Array<'read' | 'write' | 'delete' | 'update'>;
};
const role: Role = {
role: 'viewer',
permissions: ['read']
}
const userWithRole = merge(user, role)
/* valor: {
"id": "1234",
"name": "William",
"age": 36,
"email": "iwilldev@outlook.com.br",
"role": "viewer",
"permissions": [
"read"
]
} */
// tipo: User & Role
As possibilidades são infinitas
Com generics + inferência, você escreve:
- ✅ Código mais seguro
- ✅ Helpers mais inteligentes
- ✅ Implementações mais elegantes
😎
E claro: muitas libs trazem funções como essas.
Mas será que você precisa de uma lib inteira, pra resolver um problema pontual? Então domine seu código, abraçando o poder do TypeScript e tudo o que ele pode fazer pelo seu projeto!
Qual função você mais curtiu?
Me conta aí nos comentários!
🔁 Salve, compartilhe e siga para mais conteúdo