Background
I want to implement my own usePromise, e.g.
// if with filterKey(e.g `K=list`), fileNodes's type should be `FileNode` (i.e. T[K])
const [fileNodes, isOk] = usePromise(
() => {
return Promise.resolve({list:[]}) as Promise<{ list: FileNode[] }>;
},
{ initValue: [] },
"list",
);
// if with no filterKey, data's type should be `{list: FileNode[]}` (i.e. T)
const [data, isOk] = usePromise(
() => {
return Promise.resolve({list:[]}) as Promise<{ list: FileNode[] }>;
},
{ initValue: {list:[]} }
);
My problem
I use K extends keyof T
to check filterKey
and its type K
- If K is property of T, it shuld return
T[K]
- If K is not property of T, it shuld return
T
Here is the code which implements usePromise
. I found that K extends keyof T
always result in false.
How to fix my code?
import { useState, useRef, useEffect } from 'react'
interface Options<T> {
initValue?: T
one rror?: (r: () => any) => any
}
interface FileNode {}
export function usePromise<T, K>(
factory: () => Promise<T>,
options: Options<K extends keyof T ? T[K] : T> = {},
filterKey?: K,
): [K extends keyof T ? T[K] : T, boolean] {
type R = K extends keyof T ? T[K] : T;
const [state, setState] = useState<R>(
options.initValue!,
);
const isLoadingRef = useRef(false);
useEffect(() => {
factory().then((r) => {
if (filterKey) {
setState((r as any)[filterKey] as unknown as R);
} else {
setState(r as unknown as R);
}
}).catch((res) => {
if (options.onError) options.onError(res);
else throw res;
});
}, []);
return [state, isLoadingRef.current] as [R, boolean];
}
Complex solution
There is a complex solution via overloaded function. Is there any simple way?
export function usePromise<T, K extends keyof T>(
factory: () => Promise<T>,
options: Options<T[K]>,
filterKey: K,
): [K extends keyof T ? T[K] : T, boolean];
export function usePromise<T>(
factory: () => Promise<T>,
options: Options<T>,
): [T, boolean];
CodePudding user response:
Your current implementation of usePromise
infers K
to be string | undefined
when you are expecting 'list'
. You can fix this by constraining the type of K
:
export function usePromise<T, K extends keyof T | undefined>
Here is a minimal reproduction in a playground.