Вам необходим дополнительный параметр типа, представляющий фактический ключ, который будет передан. Этот параметр будет выведен в виде строкового литерала на основе значения, переданного параметру key
. С этим новым типом мы можем использовать запросы типа, чтобы получить конкретное свойство типа в типах Source
и Destination
.
Также, поскольку мы заботимся только о конкретном ключе K
, мы можем использовать его, когда мы определили тип Destination
как имеющий его (вместо указания, что Destination
должен иметь все ключи Source
). Так как нам на самом деле не важен тип свойства назначения, только то, что оно существует и что функция transformer
должна возвращать значение того же типа, что и это свойство, мы можем просто указать тип свойства в Destination
как unknown
.
interface A { a?: number };
interface B { a?: string };
function copy<
K extends keyof Source, // The extra type parameter
Source extends object,
Destination extends { [destinationKey in K]?: unknown } // Can be anything we don't really care, we only care about the K key existing
>(
source: Source,
key: K, // The key is of type K
destination: Destination,
transformer: (value: Source[K]) => Destination[K] // We use K to specify the relation between Source and Destination property type
) {
if (source[key] !== undefined) {
destination[key] = transformer ? transformer(source[key]) : source[key];
}
}
const a: A = { a: 123 };
const b: B = {};
copy(a, "a", b, (value) => value.toString());
copy(a, "a", b, (value) => value); /// error