Issue
Let's say I have a complex object containing child elements of different types:
const stores = {
A: {
dataA: 'something A',
getDataA(){
return this.dataA;
}
},
B: {
dataB: 'something B',
getDataB(){
return this.dataB;
}
}
}
To get child A
I do the following:
type TStore = typeof stores;
function getStore(key: keyof TStore){
return stores[key]
}
const child = getStore('A');
The problem is I can't reach the properties of this child because it has the following type:
const child: {
dataA: string;
getDataA(): string;
} | {
dataB: string;
getDataB(): string;
}
But I expect the child to be of the next type:
const child: {
dataA: string;
getDataA(): string;
}
There is a function that works properly:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
but requires an obj
argument, which is redundant in my case.
So how do I get my getStore
function to work the same as getProperty
?
UPDATE
In fact, I'm trying to implement some kind of helper function for MobX
that will retrieve props from combined stores. So, I have a function:
const useStores = <T extends object = any, K extends keyof T = keyof T>(key: K) => {
const stores = useContext<T>(StoreContext);
return stores[key];
};
const child = useStores<TStore>('B');
But it still returns union of subtypes. The StoreContext
is a React.Context
has any
type because I don't know the type when it is created. So, I need to cast it later.
Solution
You need to infer key
argument instead of just providing its type
const stores = {
A: {
dataA: 'something A',
getDataA() {
return this.dataA;
}
},
B: {
dataB: 'something B',
getDataB() {
return this.dataB;
}
}
}
type TStore = typeof stores;
function getStore<Key extends keyof TStore>(key: Key) {
return stores[key]
}
const child = getStore('A');
More about type inference on function arguments you can find in my blog
What if I need generic function something like this ?
Than you need to curry
it, just like I did in my article:
const stores = {
A: {
dataA: 'something A',
getDataA() {
return this.dataA;
}
},
B: {
dataB: 'something B',
getDataB() {
return this.dataB;
}
}
}
type TStore = typeof stores;
const withDictionary = <Dict extends Record<string, unknown>>(dict: Dict) =>
<Key extends keyof Dict>(key: Key) => dict[key]
const getStore = withDictionary(stores)
const child = getStore('A');
Related articles: curry react components , curry dictionary
Answered By - captain-yossarian from Ukraine
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.