ts 如何根据可选参数返回不同的泛型成员,有简单的写法吗?

2022-09-14 22:21:52 +08:00
 a132811

我想实现一个 ts 版本的简版 usePromise

  // 期望类型推导出:data 的类型是 {list: FileNode[]} (即返回 T)
  const [data, isOk] = usePromise(
    () => {
      return Promise.resolve({list:[]}) as Promise<{ list: FileNode[] }>;
    },
    { initValue: {list:[]} }
  );

  // 期望类型推导出:fileNodes 的类型是一个 FileNode 数组 (即返回 T['list'])
  const [fileNodes, isOk] = usePromise(
    () => {
      return Promise.resolve({list:[]}) as Promise<{ list: FileNode[] }>;
    },
    { initValue: [] },
    "list",
  );
  

我的 usePromise 实现如下, 利用K extends keyof T 判断如果 K 属性存在就返回T[K], 否则返回T 测试发现 ts 判断可选参数时总会失败,只会返回T.

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];
}


我知道可以增加两个 function 的类型重载解决这个问题,但是手写一遍类型重载感觉太麻烦。

请教一下有没有更简单的方法呢?

1658 次点击
所在节点    TypeScript
3 条回复
YuJianrong
2022-09-15 08:47:03 +08:00
YuJianrong
2022-09-15 08:52:04 +08:00
a132811
2022-09-15 11:55:17 +08:00
@YuJianrong 不是纠结范型。只是想找一个比 overloaded 更简单的方法。

在 statckoverflow 我找到答案啦:
https://stackoverflow.com/questions/73718892/return-different-generic-type-via-functions-optional-argument-in-typescript

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/880092

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX