如何将两个打字稿泛型联系在一起

Ser*_*hov 1 generics typescript

假设我有两种基本类型代表不共享任何数据的实体,fe 请求和对某些虚构 API 的响应。但是,从一个实体派生的每种类型都有一对另一个派生实体,fe

type Request = { token: string }
type Response = { resultCode: number }

type UserRequest = Request & { userId: number }
type UserResponse = Response & { userName: string }
Run Code Online (Sandbox Code Playgroud)

最好有一个可以将 UserRequest 和 UserResponse 联系在一起的类型,fe

type Query<Rq extends Request, Rs extends Response> = {}
type UserQuery = Query<UserRequest, UserResponse>
Run Code Online (Sandbox Code Playgroud)

并在函数签名中使用该类型而无需指定这两种类型,如下所示:

function processQuery<Q extends Query>(request: Q.Rq): Q.Rs {
    return {} as Q.Rs;
}

let userResponse = processQuery<UserQuery>({
    token: "123",
    userId: 1
});
console.log(userResponse.userName);
Run Code Online (Sandbox Code Playgroud)

这在今天的 TypeScript 中可能吗?

jca*_*alz 5

我不太了解您的用例,所以我不确定这是否是处理事情的最佳方式。也就是说,您当然可以表示这样的类型操作。这是一种方法:

type Query<RQ extends Request = Request, RS extends Response = Response> = {
    rq: RQ;
    rs: RS;
}
Run Code Online (Sandbox Code Playgroud)

请注意,Query<RQ, RS>类型必须在结构上依赖于RQRS类型参数。你真的不想要一个类型定义,比如type Foo<T> = {}它评估的类型是空的或者独立于它的类型参数。所以我所做的就是给定Query<RQ, RS>了一个rqtype属性RQ和一个rstype 属性RS

此外,我使用了泛型参数默认值,因此它Query本身就意味着Query<Request, Response>. 以下定义保持不变:

type UserQuery = Query<UserRequest, UserResponse>;
Run Code Online (Sandbox Code Playgroud)

而现在processQuery可以利用查找类型拉出rqrs物业类型Q。TypeScript 不支持用于属性查找的点表示法;相反,您使用括号表示法作用于字符串文字。无论如何,如果Q指定为Query<T, U>,则类型Q["rq"]将为T,类型Q["rs"]将为U。所以它看起来像这样:

function processQuery<Q extends Query>(request: Q["rq"]): Q["rs"] {
    return {} as Query["rs"]; // what's this?
}
Run Code Online (Sandbox Code Playgroud)

所以这就像你想要的那样:

let userResponse = processQuery<UserQuery>({
    token: "123",
    userId: 1
});
console.log(userResponse.userName);
Run Code Online (Sandbox Code Playgroud)

这似乎要求您UserQuery在对 的调用中手动指定类型userResponse,因为编译器无法Q从作为request参数传入的值中推断出(它可能可以推断出 的类型rq,但没有可以从中推断出 的位置) infer rs.) 但是由于您已经在示例代码中这样做了,我想这对您来说不是问题。

好的,希望有帮助;祝你好运!

代码链接