我正在尝试创建一个通用的API类型,它的属性是返回Promise的函数。
export type API = {
[key: string]: <Params extends any[], Response>(...params: Params) => Promise<Response>,
}
export interface UserResponse {
data: User
}
export interface User {
id: string;
name: string;
}
export const api: API = {
get: (id: User['id']): Promise<UserResponse> => Promise.resolve({
data: {
id,
name: 'name'
}
})
};
这给了我get
:上的错误
Type '(id: User['id']) => Promise<UserResponse>' is not assignable to type '<Params extends any[], Response>(...params: Params) => Promise<Response>'.
Types of parameters 'id' and 'params' are incompatible.
Type 'Params' is not assignable to type '[id: string]'.
Type 'any[]' is not assignable to type '[id: string]'.
Target requires 1 element(s) but source may have fewer.
如何使泛型类型接受id: string
作为API方法的参数?
游乐场
这是因为您使用的模板文字不正确。最简单(也是最"错误"的答案(是以下。。。这将突破许多用例,并且您的泛型。。。
export type API = {
[key: string]: (...params: any[]) => Promise<Response>,
}
相反,让走你需要的路线这个通用:
让我们问问自己API类型实际要求的是什么,它将其所有键/值对定义为具有任何数量的参数,这在技术上是正确的,但会错误地暴露API常量,从而允许您执行类似的操作
API.get() //<-- No error thrown here, since it uses the API typing correctly.
API.get(true, false, 'foo', 'bar') //<-- This also is allowed
因此,我们为什么会抛出此错误Target requires 1 element(s) but source may have fewer.
基本上概述了在API的导出端,有人可能会错误地使用它。
第一个选项:显式键入API的所有成员。这是确保API形状的首选和最安全的选项。
export type API = {
get: (id: string) => Promise<Response>
//set: (id: string) => Promise<Response>
//...
}
第二个选项:让TS推断您的API。对于不变性,指定as const
断言。
const api = {
get: (id: User['id']): Promise<Response> => Promise.resolve({
data: {
id,
name: 'name'
}
}),
} //as const
// {
// get: (id: User['id']) => Promise<Response>;
// }
第三种选择:创建一个包装函数,该函数将推断返回值,这是TS中滥用函数推断来根据赋值确定类型的常见技巧。如果您想使用TS强制API具有特定类型,特别是如果多个开发人员将接触此代码(但不想显式键入API(,这是最好的选择。就可读性而言,这也是最糟糕的选择,对那些可能不熟悉TS的人来说,这会混淆类型,并可能使追踪api
更困难
const returnAPI = <
T extends Record<string, (...args: any[]) => Promise<Response>>
>(o: T): {
[K in keyof T]: T[K]
} => o
const api = returnAPI({
get: (id: User['id']): Promise<Response> => Promise.resolve({
data: {
id,
name: 'name'
}
}),
})
我们使用extends
关键字来表示T必须具有特定的形状,在这种情况下,由扩展string
值的键组成,并且它们的值必须是始终返回Promise<Response>
的函数,这可以根据需要进行修改。
- 更多关于映射类型的阅读
查看游乐场,找到最适合您的选择。