【问题标题】:Typescript: Returning an object that returns promise type definition打字稿:返回一个返回承诺类型定义的对象
【发布时间】:2021-08-15 15:51:06
【问题描述】:

我对 TS 还很陌生,一直被这个特定问题所困扰。

我正在使用 reduce 构建一个对象,它将对象方法包装在一个函数中。我需要对象返回原始对象的键以及 apiDispatch 函数的承诺,据我所知。

任何帮助将不胜感激

const actions = {
  getSomething: ({ id }) => ({
    url: `/something/${id}`
  })
};

function buildClient() {
  const apiDispatch = async (data: any) => Promise;

  return Object.entries(actions).reduce((acc, [key, value]) => {
    acc[key] = (data: any, options: any): Promise<any> =>
      apiDispatch({
        ...value(data),
        ...options
      });
    return acc;
  }, {} as typeof actions);
}

const client = buildClient();

client.getSomething({ id: 123 }).then((res: any) => res);
  1. acc[key] 给出以下错误 - Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
  2. client.getSomething().then() then 没有赋值给类型

codeandbox 链接:

https://codesandbox.io/s/typescript-playground-export-forked-2yg63?file=/index.ts

【问题讨论】:

  • 您可以像这样使用actions: Record&lt;string, any&gt; 指定操作类型:codesandbox.io/s/… 有帮助吗?

标签: typescript typescript-typings


【解决方案1】:

据我了解,getSomething 属性末尾应该有两个参数。

const acts = {
  getSomething: ({ id }: { id: string }) => ({
    url: `/something/${id}`
  }),
  getSomethingElse: ({ name }: { name: string }) => ({
    url: `/something/${name}`
  })
} as const;

type Actions = typeof acts;

type Fn = (...args: any[]) => object

const apiDispatch = <T,>(data: T) => Promise.resolve(data);

type Convert<T extends Record<string, Fn>> = {
  [Prop in keyof T]: <Options extends object>(data: Parameters<T[Prop]>[0], options: Options) => Promise<any>
}

type O = Convert<typeof acts>

const buildClient = <
  Keys extends string,
  Value extends Fn,
  Rec extends Record<Keys, Value>
>(actions: Rec) =>
  Object.entries<Value>(actions).reduce((acc, [key, value]) => ({
    ...acc,
    [key]: <T, U>(data: T, options: U) => apiDispatch({
      ...value(data),
      ...options
    })

  }), {} as Convert<Rec>);

const client = buildClient(acts)

const result = client.getSomething({ id: 'dsf' }, {}).then(data => data) // ok
const result2 = client.getSomethingElse({ name: 'dsf' }, {}).then(data => data) // ok
const result3 = client.getSomethingElse({ id: 'dsf' }, {}).then(data => data) // expected error

友情提示,尽量避免TS突变。 Here,在我的博客中,你可以找到一些你在 TS 中改变值时可能遇到的问题

【讨论】:

    【解决方案2】:

    我只改变了一行就解决了你的问题。在这种情况下,TypeScript 不够聪明,无法理解这个地方的赋值是允许的。所以我要做的就是稍微改变一下类型(比如 Sean),然后使用 TypeScript 中的as 运算符将其改回来。

    这就是代码现在的样子:

    type OverrideReturn<T extends (...args: any[]) => any, R> = (...args: Parameters<T>) => R;
    
    const actions = {
      getSomething: ({ id }: { id: any }) => ({
        url: `/something/${id}`
      })
    };
    
    function buildClient() {
      const apiDispatch = async (data: any) => ({ name: "test object" });
    
      return Object.entries(actions).reduce((acc, [key, value]) => {
        acc[key] = (data: any, options: any): Promise<any> =>
          apiDispatch({
            ...value(data),
            ...options
          });
        return acc;
      }, {} as Record<string, any>) as {} as { [K in keyof typeof actions]: OverrideReturn<typeof actions[K], Promise<any>> };
    
    }
    
    const client = buildClient();
    
    client.getSomething({ id: 123 }).then((res: any) => res);
    

    TypeScript Playground

    【讨论】:

      【解决方案3】:

      更新

      您收到此错误的原因是您定义了{} as typeof actions。有了这个,Typescript 期望您的 acc 与您的 actions 相同。但由于您希望您的 acc 有所不同,因此您需要为它定义一个新的返回类型。

      1. buildClient定义新类型,如果actions类型可以不时更改,这里可以使用Typescript Generic Type。使用下面的新类型定义,Client 类型将是一个对象,其属性键与泛型类型相同,每个键的值将是一个返回 Promise 的函数。

      type Client&lt;T&gt; = Record&lt;keyof T, (data: any, options?: any) =&gt; Promise&lt;any&gt;&gt;;

      1. buildClient定义为接收泛型类型T的泛型函数

      function buildClient&lt;T&gt;()

      1. 输入acc[key as keyof T],让Typescript 知道属性键将来自泛型类型T
      2. 输入{} as Client&lt;T&gt;,让Typescript 知道{}Client&lt;T&gt; 类型而不是空对象。
      3. 当您调用buildClient 时,只需在其旁边输入&lt;typeof actions&gt;,这样Typescript 就会将所有泛型类型T 替换为typeof actions

      const client = buildClient&lt;typeof action&gt;()

      在所有这些更改之后,Typescript 现在将理解 client 只有一个属性 getSomething 并且它是一个返回承诺的函数。所以,最终的代码是:

      type Client<T> = Record<keyof T, (data: any, options?: any) => Promise<any>>;
      
      const actions = {
        getSomething: ({ id }) => ({
          url: `/something/${id}`
        })
      };
      
      function buildClient<T>(): Client<T> {
        const apiDispatch = async (data: any) => Promise;
      
        return Object.entries(actions).reduce((acc, [key, value]) => {
          acc[key as keyof T] = (data: any, options: any): Promise<any> =>
            apiDispatch({
              ...value(data),
              ...options
            });
          return acc;
        }, {} as Client<T>);
      }
      
      const client = buildClient<typeof actions>();
      
      client.getSomething({ id: 123 }).then((res: any) => res);
      

      【讨论】:

      • 返回的对象将不再具有类型安全性。
      • @Sean 感谢您的回复。这确实抑制了类型错误,但我希望能够键入客户端。并让智能感知建议动作对象中的功能,例如client.getSomething({ id: 123 }) 使用 as typeof actions 的问题是 TS 不知道 client.getSomething() 函数应该返回一个 Promise
      • @James 对不起,我误解了你的问题。我已根据您的反馈更新了我的答案。
      猜你喜欢
      • 2019-04-25
      • 2021-06-17
      • 2023-03-16
      • 2022-01-14
      • 2015-11-27
      • 2018-12-28
      • 2019-09-01
      • 2017-10-21
      • 2020-01-04
      相关资源
      最近更新 更多