首页 > 解决方案 > 如何根据构造函数选项设置方法返回类型?

问题描述

在实现速率限制的get之上开发一个开源抽象。

一切都很好,除了我无法弄清楚如何将返回类型设置为使用构造函数选项提供的 、 、 和post方法getpatch请参阅参考资料)。putdeleteresponseTyperesponseType: options?.responseType ?? "json",

我使用覆盖来使用responseType方法参数(如果提供)设置返回类型,但我不知道如何为构造函数选项执行此操作。真的可以用这个!

顺便说一句,除了这里的答案,如果您想为该项目做出贡献,请随时向https://github.com/sunknudsen/http提交 PR。

type ResponseType = "buffer" | "json" | "text"
interface HTTPOptions { responseType?: ResponseType }
interface HTTPResponse<T = unknown> { body: T }

class HTTP {
  constructor(public options: HTTPOptions) {    
    this.options.responseType = options.responseType ?? "json"; // Default response type is JSON
  }  
  public get( url: string, options: HTTPOptions & { responseType: "buffer" }): Promise<HTTPResponse<Buffer>>
  public get(url: string, options: HTTPOptions & { responseType: "json" }): Promise<HTTPResponse<JSON>>
  public get(url: string, options: HTTPOptions & { responseType: "text" }): Promise<HTTPResponse<string>>
  public get(url: string, options?: HTTPOptions): Promise<HTTPResponse>
  public get(url: string, options?: HTTPOptions): Promise<HTTPResponse> {
    return null!
  }  
}


let http = new HTTP({ responseType: "text" });

http.get("", { responseType: "text" }) // string 
http.get("") // expected string, this does not work

游乐场链接

标签: typescript

解决方案


问题的第一部分是在构造函数中捕获类型信息。我们需要知道responseType创建类时传递的类型。每当我们需要捕获呼叫站点信息时,通常可以使用泛型类型参数。

所以我们可以将类更改为:

class HTTP<TResponseDefault extends ResponseType = "json"> {
  constructor(public options: HTTPOptions & { responseType?: TResponseDefault }) {    
    this.options.responseType = options.responseType ?? "json" as TResponseDefault; // Default response type is JSON
  }
}  

这将确保我们在方法中有必要的信息,因为我们在传入HTTP时捕获了任何已创建对象的类型:responseType

let httpJson = new HTTP({}); //  HTTP<"json">
let httpText = new HTTP({ responseType: "text"}); //  HTTP<"text">

现在我们有了这些信息,我们可以看到如何使返回类型依赖于它。虽然重载通常是一个很好的第一种方法,但在这种情况下,因为默认值可以来自类或调用,并且因为有很多选项和方法,所以我会采用另一种方法。

我们可以做的是使用泛型类型参数使方法泛型以捕获响应类型。此泛型类型参数将默认为类类型参数。因此,如果在调用中未指定响应类型,TResponseDefault则将使用。然后我们可以使用这个类型参数来索引一个我们从响应类型映射到实际类型的接口:


interface TypeToResponseType {
  "buffer" : HTTPResponse<Buffer>
  "json" : HTTPResponse<object>
  "text" : HTTPResponse<string>
}

class HTTP<TResponseDefault extends ResponseType = "json"> {
  constructor(public options: HTTPOptions & { responseType?: TResponseDefault }) {    
    this.options.responseType = options.responseType ?? "json" as TResponseDefault; // Default response type is JSON
  }  

  public get<TResponse extends ResponseType = TResponseDefault>(url: string, options?: HTTPOptions & { responseType?: TResponse }): Promise<TypeToResponseType[TResponse]>
  public get(url: string, options?: HTTPOptions): Promise<HTTPResponse> {
    return null!
  }  
}


let httpJson = new HTTP({}); //  HTTP<"json">
let httpText = new HTTP({ responseType: "text"}); //  HTTP<"text">

let a = httpJson.get("", { responseType: "text" }) // string 
let b = httpJson.get("") // object
let c = httpText.get("") // string 

游乐场链接


推荐阅读