Reputation: 187
I want to create a function that takes in three parameters: an object, a key on the object, and a new value. I want the function to be able to access a value using the key parameter, perform some operation based on that data, and then set the property to the new value.
This was the initial approach I took:
function someOperation(obj: object, key: keyof obj, newValue: typeof obj[key])
const obj = { a: 'hello', b: 2 };
// TypeScript should show an error here if the third parameter is not a string
someOperation(obj, 'a', 'goodbye');
console.log(obj); // should be { a: 'goodbye', b: 2 }
Unfortunately, I get some undescriptive errors where I specify the type of newValue
. I could just set its type to any
to fix the errors, but I really want to have stronger type checking. Does anyone have any ideas on how I could solve this problem? Perhaps there are different ways to approach this. Thanks.
Upvotes: 1
Views: 1204
Reputation: 2201
@zixiCat's solution is perfectly valid and works. But it may seem far-fetched and is difficult to debug the error messages like
Argument of type '["a", "1"]' is not assignable to parameter of type 'TParams<{ a: number; b: string; }>'.
Type '["a", "1"]' is not assignable to type '["a", number]'.
Type 'string' is not assignable to type 'number'.(2345)
There is more elegant and simple solution possible with "more-friendly" errors.
function someOperation<T extends {}, K extends keyof T>(
obj: T,
key: K,
value: T[K]
) {
//
}
const test = { a: 1, b: '1' };
someOperation(test, 'a', 1); // OK
someOperation(test, 'b', '1'); // OK
someOperation(test, 'a', '1'); // Error
someOperation(test, 'c', '1'); // Error
Upvotes: 2
Reputation: 1059
The core idea is union-types and using Arr to wrap them like that:
type paramsArr = [ObjType, 'a', number] | [ObjType, 'b', string]
And then you can try this one:
type TParamsMap<T> = {
[P in keyof T]: [P, T[P]] // [key, newValue];
};
type TParams<T> = TParamsMap<T>[keyof TParamsMap<T>]
function someOperation<T>(obj: T, ...rest: TParams<T>) {
obj[rest[0]] = rest[1];
}
const test = { a: 1, b: '1' };
someOperation( test, 'a', 1); // OK
someOperation( test, 'b', '1'); // OK
someOperation( test, 'a', '1'); // Error
someOperation( test, 'c', '1'); // Error
Upvotes: 2