I am trying to create a "assign default" function in typescript, where it loops through the keys of the source, and if that value by the same key is nullish in the target, it will use the value from the source instead. Here's my attempt:
const assignDefault = <T, U>(target: T, source: U): T & U => {
Object.keys(source).forEach(key => {
// typecasted as Object.keys returns string[]
const prop = target[key as keyof T]
if (typeof prop === 'undefined' || typeof prop === 'null') {
// Error: Type 'U[keyof U]' is not assignable to type 'T[keyof T]'.
target[key as keyof T] = source[key as keyof U]
}
})
return target // Error: Type 'T' is not assignable to type 'T & U'.
}
I borrowed the generics from how Object.assign is typed in typescript: ObjectConstructor.assign<T, U>(target: T, source: U): T & U; But I couldn't find a way to get around these errors.
CodePudding user response:
As you're always returning a value with the type of T, you should change your type signature to:
const assignDefault = <T, U>(target: T, source: U): T => { ... }
Object.assign has the type of T & U because it also adds non-existing keys in the target from the source. This is not the case with your function.
CodePudding user response:
This is what I came up with.
Couple things to note:
typeof will never return "null" see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
second thing the type T & U expects a combination of both the target and source. You can achieve that fairly easily by using the spread operator to merge them as shown in my example.
And finally when assigning to your target, it is possible that the key of the target does not exist in the source so adding a check (I used a turnery operator) helps resolve that error.
const assignDefault = <T, U>(target: T, source: U): T & U => {
Object.keys(source).forEach(key => {
const prop = target[key as keyof T]
if (typeof prop === 'undefined') {
/**
* if there is a value of source[key] use that
* otherwise keep using target[key]
*/
target[key as keyof T] = source[key] ? source[key] : target[key];
}
});
/**
* merge the 2 objects to achieve type T & U (if you want).
*
* honestly this might be shorthand for your whole function.
*/
return {
...source,
...target
}
}
