Reputation: 36189
Say I have
interface User {
name: string;
age: number;
}
interface Car {
year: number;
model: string;
}
interface Action<T> {
assign<K extends keyof T>(key: K, value: T[K]): void;
}
This allows me to do:
const userActions: Action<User> = ...;
const carActions: Action<Car> = ...;
userActions.assign('age', 1); // all good
userActions.assign('foo', 2); // error that `foo` does not exist
userActions.assign('age', 'foo'); // error that type string is not assignable to age
carActions.assign(...); // same behavior for car
Now I want to create auxiliary methods that can be passed to assign
, for example:
const logAndAssign = (key, value): void;
And I want to be able to do
userActions.assign(logAndAssign('age', 1));
// etc
And so I want these aux methods logAndAssign
to get the type passed to them. How can I achieve this?
Upvotes: 3
Views: 83
Reputation: 249506
You can't call a function with a single argument directly, you could use apply
but apply
call would not be type safe and calling logAndAssign
would imply you passing the type arguments explicitly:
const logAndAssign = function <T, K extends keyof T>(key: K, value: T[K]): [K, T[K]] {
console.log(`${key} ${value}`);
return [key, value];
};
userActions.assign.apply(userActions, logAndAssign<User, 'age'>('age', 1));
A better solution would be replace the assign
function on Action
and then restore it:
function withLogging<T>(a: Action<T>, doStuff: (a: Action<T>) => void) {
let oldAssign = a.assign;
// Replace the assign function with a logging version that calls the original
a.assign = function <K extends keyof T>(key: K, value: T[K]): void {
console.log(`${key} ${value}`);
oldAssign.call(this, key, value);
};
try {
doStuff(a);
} finally {
//Restore the original assign
a.assign = oldAssign;
}
}
// Single call
withLogging(userActions, u => u.assign('age', 10));
// Multiple calls
withLogging(userActions, u => {
u.assign('age', 10);
u.assign("name", 'd');
});
Upvotes: 2