我是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);
acc[key]
给出以下错误-Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
client.getSomething().then()
则不分配给该类型
链接到codesandbox:
https://codesandbox.io/s/typescript-playground-export-forked-2yg63?file=/index.ts
据我所知,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。在这里,在我的博客中,你可以找到一些你可能会遇到的问题,改变TS 中的值
我只更改了一行就解决了您的问题。在这种情况下,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游乐场
更新
出现此错误的原因是您定义了{} as typeof actions
。有了这一点,Typescript希望您的acc
与actions
相同。但是,由于您希望acc
有所不同,因此您需要为其定义一个新的返回类型。
- 为
buildClient
定义新类型,如果actions
类型可以随时更改,则可以在此处使用Typescript Generic type。使用下面的新类型定义,Client
类型将是一个与泛型类型具有相同属性键的对象,并且每个键的值将是返回Promise的函数
type Client<T> = Record<keyof T, (data: any, options?: any) => Promise<any>>;
- 将
buildClient
定义为接收泛型类型T
的泛型函数
function buildClient<T>()
- 放置
acc[key as keyof T]
,让Typescript知道属性键将来自泛型类型T
- 放
{} as Client<T>
,让Typescript知道{}
是Client<T>
类型,而不是空对象 - 当您调用
buildClient
时,只需将<typeof actions>
放在它旁边,这样Typescript就会用typeof actions
替换所有泛型类型T
const client = buildClient<typeof action>()
在所有这些更改之后,Typescript现在将理解client
只有一个属性getSomething
,并且它是一个返回promise的函数。因此,最终代码将是:
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);