Будет трудно сделать этот тип безопасным , потому что TypeScript не может действительно обработать операцию типа более высокого порядка, в результате чего обратный вызов добавляет одно возвращаемое свойство к возвращаемому значению каждый раз, когда он вызывается, и в итоге возвращает значение типа T & U
. Первый вызов обратного вызова будет иметь agg
типа U
и возвращать U & Pick<T, K>
для некоторого синглтона K extends keyof T
, а последний вызов обратного вызова будет иметь agg
типа U & Omit<T, L>
для какого-то другого синглтона L extends keyof T
и возврат T & U
. Это может быть забавное упражнение, чтобы увидеть, как близко мы можем подойти, но гораздо проще и продуктивнее просто сказать: проверка безопасности типов этой функции выше уровня оплаты компилятора.
С другой стороны, должно быть достаточно просто использовать некоторые разумные утверждения типа , чтобы сообщить компилятору, какие типы следует ожидать:
const merge = <T extends Record<keyof T, object>, U extends Record<keyof U, object>>(
a: T, b: U
) => {
return (
Object.entries(a) as Array<[keyof T, T[keyof T]]>
).reduce((agg, [key, value]) => ({
...agg,
[key]: {
...agg[key],
...value,
}
}), b as T & U)
}
Здесь я использовал два утверждения. Во-первых, Object.values()
вернет Array<[keyof T, T[keyof T]]>
, что более или менее верно; каждый элемент массива будет парой ключей и значений T
. Во-вторых, b
и, следовательно, agg
равно T & U
. Это в значительной степени неверно, но в итоге становится в основном правдой. Я говорю «в основном», потому что операторы распространения только приблизительно ведут себя как пересечения, особенно если у вас есть необязательные свойства или примитивные типы, поэтому действуйте на свой страх и риск. В любом случае утверждение b
как T & U
также подразумевает, что merge()
возвращает T & U
, что вам и нужно.
Также обратите внимание, что я ограничил T
и U
быть object
s, которые содержат object
s, так что вы случайно не используете merge()
, где расширение на два слоя было бы ошибкой:
merge({ a: "" }, { a: { b: 1 } }); // error!
// ~ <-- string is not object
Хорошо, надеюсь, это поможет; удачи!
ссылка на игровую площадку