【发布时间】:2019-07-05 05:23:31
【问题描述】:
我很难为这个问题命名 - 愿意改变它。
我是 typescript 的新手,我正在尝试以通用、类型安全且非常可扩展的方式使用 API。
从RESTyped 获得灵感,我定义了一个通用的“API 定义”接口:
interface ApiBase {
[route: string]: ApiRoute
}
interface ApiRoute {
query: { [key: string]: string }
body: any
response: any
}
interface ApiSpec {
[route: string]: {
[method: string]: ApiRoute
}
}
这可用于定义多个 API 端点的类型,如下所示:
interface MyApi extends ApiSpec {
"/login": {
"POST": {
body: {
username: string,
password: string
},
response: {
token: string
}
}
},
"/user": {
"GET": {
query: {
"username": string
},
response: {
"email": string,
"name": string
}
}
}
}
我怀疑泛型类可以使用这些类型,并为它们提供类型安全的方法。比如:
const api = ApiService<MyApi>();
api.post("/login", {
// This body is typesafe - won't compile if it doesn't match the spec
username: "johnny99",
password: "hunter2"
});
如果对象与MyApi 接口中定义的body 不匹配,则post() 方法将无法编译。
不幸的是,我很不知道从这里去哪里。像这样的:
class ApiService<T> {
post(route: string, body: T[route].body): T[route].response {
// todo
}
}
这显然无法编译。如何访问MyApi 接口中的子类型? T[route].body 绝对是错误的。我该怎么做?
干杯
编辑 ------------------------------------------
我做了一些阅读,我想我正在取得进展!
这适用于打字稿游乐场:
class ApiService<API extends ApiSpec> {
async post<Path extends Extract<keyof API, string>>(
route: Path,
data: API[Path]["POST"]["body"]
): Promise<API[Path]["response"]> {
const resp = await fetch(route, {
method: "POST",
body: JSON.stringify(data),
});
return await resp.json();
}
}
并且在调用存在的路由时完美运行:
const api = new ApiService<MyApi>();
// Will give an error if the wrong "body" is passed in!
api.post("/login", {
username: "johnny99",
password: "rte"
});
但它也在调用不存在的路由时起作用,这不是我想要发生的。
// Should error, but doesn't!
api.post("/bad", {
whatever: ""
});
我也有点担心我的post() 实现——当resp.json() 给出的对象与类型定义中定义的对象不同时会发生什么?它会引发运行时错误——我应该总是在 try/catch 守卫中调用它,还是我可以以某种方式让 Promise 失败?
【问题讨论】:
标签: typescript api interface type-safety typesafe