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 StoreContextis 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.