Reputation: 322
I have an environment where certain objects which have an id
property expire every "tick" and need to be recalled using getObjectById
. I would like to implement a setter method to refresh a property of something by mapping thing.property => getObjectById(thing.property.id)
. Ideally, I would like this method to take a thing T
and a key K
, where T[K]
can be an object with an id (HasID
) or and array of them (HasID[]
).
I believe I'm close to a solution, but it's not quite correct yet. What I currently have is listed below (a static method on a class called $
):
static refresh<T extends {[K in keyof T]: HasID}, K extends keyof T>(thing: T, key: K): void;
static refresh<T extends {[K in keyof T]: HasID[]}, K extends keyof T>(thing: T, key: K): void;
static refresh<T extends {[K in keyof T]: HasID | HasID[]}, K extends keyof T>(thing: T, key: K): void {
if (_.isArray(thing[key])) {
thing[key] = _.map(thing[key] as HasID[], s => getObjectById(s.id)) as HasID[];
} else {
thing[key] = getObjectById(thing[key].id) as HasID;
}
}
For example, the desired behavior for foo: {bar: HasID, baz: HasID[], biz: string[]}
would be:
$.refresh(foo, 'bar') // foo.bar = getObjectById(foo.bar.id)
$.refresh(foo, 'baz') // foo.baz = _.map(foo.baz, x=>getObjectById(x.id))
$.refresh(foo, 'biz') // error: foo.biz is not HasID or HasID[]
$.refresh(foo, 'boo') // error: 'boo' is not a key of foo
Could someone point me in the right direction to correctly constrain the type of T[K]
?
Upvotes: 1
Views: 284
Reputation: 250106
You are close, the problem is that not every key of T
has to be HasID
or HasID[]
, only the one specified by K
class $ {
static refresh<T extends { [P in K]: HasID }, K extends string>(thing: T, key: K): void;
static refresh<T extends { [P in K]: HasID[] }, K extends string>(thing: T, key: K): void;
static refresh<T extends { [P in K]: HasID | HasID[] }, K extends string>(thing: T, key: K): void {
}
}
let foo: { bar: HasID, baz: HasID[], biz: string[] };
$.refresh(foo, 'bar') // foo.bar = getObjectById(foo.bar.id)
$.refresh(foo, 'baz') // foo.baz = _.map(foo.baz, x=>getObjectById(x.id))
$.refresh(foo, 'biz') // error: foo.biz is not HasID or HasID[]
$.refresh(foo, 'boo') // error: 'boo' is not a key of foo
Also instead of { [P in K]: HasID }
you can use Record<K, HasId>
, I left your original version to make it easier to track the differences. Using record the sigantures would be:
class $ {
static refresh<T extends Record<K, HasID>, K extends string>(thing: T, key: K): void;
static refresh<T extends Record<K, HasID>[], K extends string>(thing: T, key: K): void;
static refresh<T extends Record<K, HasID | HasID[]>, K extends string>(thing: T, key: K): void {
}
}
Upvotes: 3