【发布时间】:2019-11-04 15:51:35
【问题描述】:
我想将“元数据”嵌入到用于创建类型安全 REST 客户端的类型中。这个想法是使用链接中的类型元数据来推断在 API 调用中使用的正确端点模式。例如。
type Schema = {
users: {
GET: {
query: { userId: string };
};
};
posts: {
POST: {};
};
};
type User = {
self: Link<"users">;
};
const user: User = { self: "https://..." };
http(user.self, "GET", { userId: 1 });
我能够使用蛮力条件类型来解决这个问题。
例如
type Routes = "users" | "posts";
type Verbs<R> = R extends "users" ? "GET" : never;
type Query<R, V> = R extends "users"
? V extends "GET"
? { queryId: string }
: never
: never;
但这会导致难以手动输入的规范化类型模型。相反,我想使用非规范化类型,例如
type Schema = {
users: {
GET: {
query: { userId: string };
};
};
posts: {
POST: {};
};
};
使用这样的类型:
type Query<
S,
RN extends keyof S,
VN extends keyof S[RN]
> = OpQuery<S[RN][VN]>;
除了最后和关键位之外,我能够完成大部分工作,从链接类型推断路由名称:
type Schema = {
users: {
GET: {
query: { userId: string };
};
};
posts: {
POST: {};
};
};
type Link<R extends keyof Schema> = string;
type LinkRouteName<L> = L extends Link<infer R> ? R : never;
type name = LinkRouteName<Link<"users">>;
预期:名称 ===“用户”
实际:名称 === "用户" | “帖子”
【问题讨论】:
-
type Link<R extends keyof Schema> = string;对R没有任何作用,所以我很困惑。您是否尝试获取 branded primitive 以便编译器记住该字符串以某种方式“意味着”"users"调用而不是"posts"调用? -
喜欢this?
-
@jcalz 是的,这是正确的,尽管它不是关于品牌,而是更多关于类型元数据。 “R”对 Link 类型没有任何作用,它只是传达此 Link 指向特定路由的元数据。我们可以使用该类型元数据为该路由端点提供动词、查询参数和正文的类型安全列表。
-
@jcalz 看看你的例子,看来你得到了正确的推断。尝试映射回我的解决方案以确认这是有效的。
-
@jcalz 好的,我明白了。问题是 Typescript 将我的 Link 类型视为字符串,而不管我声明它的类型信息如何。但是通过“&”,类型系统现在尊重“R”具有一定意义的事实,并且类型不仅仅是一个字符串。好的!谢谢。
标签: typescript type-inference conditional-types