I'm struggling to get a complex type functionality out of this updateArray
generic function I am building:
// Updates an object array at the specified update key with the update value,
// if the specified test key matches the test value.
// Optionally pass testFailValue to set a default value if the test fails.
// Note that by passing a testFailValue ALL elements in the array will be updated at the specified update property.
// If it is omitted only elements passing the test will be updated.
export const updateArray = <T, U, V>(options: {
array: Array<T>
testKey: keyof T
testValue: U
updateKey: keyof T
updateValue: V
testFailValue?: V
}): Array<T> => {
const {
array,
testKey,
testValue,
updateKey,
updateValue,
testFailValue,
} = options
return array.map(item => {
if (item[testKey] === testValue) {
item[updateKey] = updateValue
} else if (testFailValue !== undefined) {
item[updateKey] = testFailValue
}
return item
})
}
TypeScript will complain here in both the if statement and the two assignment statements, but in a call signature, it won't complain and is the strict type checking I am looking for exactly, ex:
interface IMyInterface {
propertyA: string
prepertyB: boolean
}
updateArray<IMyInterface, IMyInterface['propertyA'], IMyInterface['propertyB']>({
array: state.editors[editor].editorSettings,
testKey: "propertyA",
testValue: 'someValue',
updateKey: "propertyB",
updateValue: true,
testFailValue: false
})
If I omit types U
and V
, and replace them with T[keyof T]
Typescript won't complain:
export const updateArray = <T>(options: {
array: Array<T>
testKey: keyof T
testValue: T[keyof T]
updateKey: keyof T
updateValue: T[keyof T]
testFailValue?: T[keyof T]
}): Array<T> => {
const {
array,
testKey,
testValue,
updateKey,
updateValue,
testFailValue,
} = options
return array.map(item => {
if (item[testKey] === testValue) {
item[updateKey] = updateValue
} else if (testFailValue !== undefined) {
item[updateKey] = testFailValue
}
return item
})
}
but this isn't entirely correct either. T[keyof T]
is too flexible: I could be assigning the 'wrong' type to a given property (for example, in the example given, a boolean value to a property that should only be holding strings, or vice-versa). Obviously, this reassigning type behavior is fine in JavaScript (which is one reason why TypeScript won't complain), but undesired for this function I am crafting. Really what I need is some sort of typeof T[specific key]
, to ensure that testValue
, updateValue
, and testFailValue
correspond with the right types, but the specific key
can change depending on the actual type of T
.
Can something like this be done?
You can add a constraint on
U
so that it is a subset of the keys ofT
usingextends
.V
could represent theupdateKey
type and also have the same constraint.Simplifying your problem to a funtion
updateObject
instead ofupdateArray
it would become: