Skip to content

Instantly share code, notes, and snippets.

@michalczukm
Last active March 25, 2022 08:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michalczukm/bc4d7c5e778793c10fe02ee48af45f68 to your computer and use it in GitHub Desktop.
Save michalczukm/bc4d7c5e778793c10fe02ee48af45f68 to your computer and use it in GitHub Desktop.
How to type safe merge objects which identical types in TypeScript
type IfEquals<T1, T2, IfEqual = T2, Else = never> = (<
Interim
>() => Interim extends T1 ? true : false) extends <
Interim
>() => Interim extends T2 ? true : false
? IfEqual
: Else;
type Entries<T> = {
[K in keyof T]: [K, T[K]];
}[keyof T][];
// Unfortunately `Object.entries` isn't well typed :/
// to avoid errors in its usage - here is small type-safe wrapper
function entries<T>(obj: T): Entries<T> {
return Object.entries(obj) as any;
}
function mergeObjects<T1, T2 extends T1, TResultField>(
first: T1,
second: IfEquals<T1, T2>,
merge: (first: T1, second: IfEquals<T1, T2>, key: keyof T1) => TResultField
): { [Key in keyof T1]: TResultField } {
return entries(first).reduce(
(acc, [key]) => ({
...acc,
[key]: merge(first, second, key),
}),
{} as { [Key in keyof T1]: TResultField }
);
}
// === example usage ====
const LABELS: Labels = {
a: 'label a',
b: 'label b',
};
const VALUES: Values = {
a: 'value a',
b: 'value b',
};
type Labels = {
a: string;
b: string;
};
type Values = {
a: string;
b: string;
};
const result = mergeObjects(LABELS, VALUES, (label, value, key) => ({
label: label[key],
value: value[key],
}));
console.log('result:', result);
// result: {
// a: { label: 'label a', value: 'value a' },
// b: { label: 'label b', value: 'value b' }
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment