Quiero una función que dados varios objetos que no tienen ninguna key en común, genere un único objeto con todas las propiedades de cada objeto. Quiero que sea en Typescript con el mayor tipado posible.
Perfecto—esto es básicamente un merge de objetos disjuntos con inferencia fuerte de tipos.
La clave es lograr que el resultado sea la intersección de todos los tipos de entrada.
🧠 Objetivo
merge(
{ a: 1 },
{ b: "x" },
{ c: true }
)
👉 resultado (y tipo):
{ a: number; b: string; c: boolean }
✅ Implementación con tipado fuerte
// Convierte una unión en intersección
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends
(k: infer I) => void
? I
: never;
// Helper para mejorar legibilidad del tipo final
type Simplify<T> = { [K in keyof T]: T[K] };
export function mergeObjects<
T extends readonly Record<PropertyKey, unknown>[]
>(
...objs: T
): Simplify<UnionToIntersection<T[number]>> {
return Object.assign({}, ...objs);
}
🔥 Uso
const result = mergeObjects(
{ a: 1 },
{ b: "hola" },
{ c: true }
);
👉 Tipo inferido:
{
a: number;
b: string;
c: boolean;
}
🧠 Por qué funciona
1. T[number]
T = [{a}, {b}, {c}]
T[number] = {a} | {b} | {c}
2. UnionToIntersection
{a} | {b} | {c}
→ {a} & {b} & {c}
3. Resultado final
{ a } & { b } & { c }
→ { a, b, c }
⚠️ Suposición importante
Esto funciona perfecto porque vos garantizás:
❗ “no tienen ninguna key en común”
Si hubiera conflicto:
{ a: number } & { a: string }
👉 a → never 💥
🚀 Bonus: hacer que falle si hay keys duplicadas
Si querés máxima seguridad, se puede agregar validación de tipos para evitar colisiones (más avanzado, pero posible).
🧠 Alternativa simple (menos estricta)
function mergeLoose(...objs: object[]) {
return Object.assign({}, ...objs);
}
👉 funciona, pero:
- ❌ pierde inferencia precisa
-
❌ tipo:
object
🧩 TL;DR
✔ Usás:
-
T[number]→ unión de tipos -
UnionToIntersection→ intersección -
Object.assign→ runtime
👉 Resultado:
tipo exacto con todas las propiedades combinadas
Si querés, en el siguiente paso puedo darte:
👉 versión que detecta conflictos de keys en compile-time
👉 o una que hace merge profundo (deep merge) con tipado fuerte (esto ya es nivel avanzado heavy 😄)
Buenísimo—esto es un clásico de “reverse lookup” tipado.
La clave es que el tipo de retorno sea solo las keys cuyo valor coincide con el tipo del valor que pasás.
🧠 Objetivo
const obj = {
a: 1,
b: "x",
c: 1,
d: true
};
keysByValue(obj, 1);
// → ["a", "c"]
// tipo: ("a" | "c")[]
✅ Implementación bien tipada
// Extrae las keys de T cuyos valores son asignables a V
type KeysMatching<T, V> = {
[K in keyof T]: T[K] extends V ? K : never
}[keyof T];
export function keysByValue<
T extends Record<PropertyKey, unknown>,
V
>(
obj: T,
value: V
): KeysMatching<T, V>[] {
const result: KeysMatching<T, V>[] = [];
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const k = key as keyof T;
// comparación en runtime
if (obj[k] === value) {
result.push(k as KeysMatching<T, V>);
}
}
}
return result;
}
🔥 Uso con inferencia fuerte
const obj = {
a: 1,
b: "x",
c: 1,
d: true
} as const;
const r = keysByValue(obj, 1);
👉 tipo de r:
("a" | "c")[]
⚠️ IMPORTANTE (muy importante para el tipado)
Para que funcione bien necesitás:
as const
👉 si no:
const obj = {
a: 1,
b: "x"
};
TypeScript infiere:
{ a: number; b: string }
Entonces:
keysByValue(obj, 1)
👉 tipo:
("a" | "b")[]
💥 porque number podría coincidir con más cosas
🧠 Por qué funciona
1. Mapped type
[K in keyof T]: T[K] extends V ? K : never
👉 filtra keys
2. Indexación final
[keyof T]
👉 convierte en unión:
"a" | "c"
🚀 Variante más estricta (igualdad exacta)
Si querés evitar matches amplios (number vs 1):
type KeysExactlyMatching<T, V> = {
[K in keyof T]: [T[K]] extends [V]
? [V] extends [T[K]]
? K
: never
: never
}[keyof T];
🧩 TL;DR
✔ devuelve las keys cuyo valor coincide
✔ tipado fuerte usando KeysMatching
✔ mejor precisión con as const
Si querés, puedo darte una versión:
👉 que soporte comparación custom (deepEqual, por ejemplo)
👉 o que devuelva directamente un Record filtrado en lugar de un array (muy útil en pipelines)
No hay comentarios:
Publicar un comentario