打字稿:返回一个返回承诺类型定义的对象

时间:2021-05-27 11:28:37

标签: typescript typescript-typings

我对 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() 然后没有分配给类型

链接到代码和框:

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

3 个答案:

答案 0 :(得分: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 中改变值可能会遇到的一些问题

答案 1 :(得分:0)

我只更改了一行就设法解决了您的问题。在这种情况下,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

答案 2 :(得分:-1)

更新

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

  1. buildClient 定义新类型,如果 actions 类型可以不时更改,您可以在此处使用 Typescript Generic Type。使用下面的新类型定义,Client 类型将是一个对象,该对象具有与泛型类型相同的属性键,每个键的值将是一个返回 Promise 的函数。
<块引用>

type Client<T> = Record<keyof T, (data: any, options?: any) => Promise<any>>;

  1. buildClient 定义为接收泛型类型 T 的泛型函​​数
<块引用>

function buildClient<T>()

  1. 输入 acc[key as keyof T],让 Typescript 知道属性键将来自泛型类型 T
  2. 输入 {} as Client<T>,让 Typescript 知道 {}Client<T> 类型而不是空对象。
  3. 当您调用 buildClient 时,只需将 <typeof actions> 放在它旁边,以便 Typescript 将所有泛型类型 T 替换为 typeof actions
<块引用>

const client = buildClient<typeof action>()

在所有这些更改之后,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);