When it comes to this scenario, key
is not just simply keyof Partial<T>
- its type actually extends keyof P
, where P
extends Partial<T>
. It's important to note that P
may contain different keys than T
, which means that using key
as a property for T
is not possible in this context.
If you prefer not to add new properties to T
, then there's no need for the type parameter P
since it can be automatically deduced from T
itself. Here are two solutions, depending on whether you want obj
to receive undefined
values from part
or not:
// no undefined values assigned from part
function updateWithPartial<T, K extends keyof T>(obj: T, part: Partial<T>, key: K) {
if (part[key] === undefined) throw new Error() // throw an error or handle it differently...
return obj[key] = part[key] as T[K] // helps the compiler understand that part[key] must be defined here
}
// possible undefined values assigned from part
function updatePartialWithPartial<T, K extends keyof T>(obj: Partial<T>, part: Partial<T>, key: K) {
return obj[key] = part[key]
}
Now, let's run some tests:
declare const foo: { a: string }
const res = updateWithPartial(foo, { a: "bar" }, "a") // string
const res2 = updatePartialWithPartial(foo, { a: "bar" }, "a") // string | undefined
const res22 = updatePartialWithPartial(foo, {}, "a") // string | undefined
Try it out in the Playground