理解复杂的类型脚本类型声明

Posted

技术标签:

【中文标题】理解复杂的类型脚本类型声明【英文标题】:Understanding complex Type Script Type declaration 【发布时间】:2019-04-18 13:36:15 【问题描述】:

问题是关于如何使用对象类型发送标头,而不是HTTPClient声明中提供的HttpHeaders。

在使用 Angular HttpClient 时,我想用 Object 传递标头,我不明白如何定义 [header: string]: string | 类型的对象string[]; 请帮助我理解这个对象声明。同样,我也面临 HttpParams。我的代码方法如下。

getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): Observable<LoggedInUserResponse> 
   return this.http.get<LoggedInUserResponse>(`$environment.apiBaseUrl/auth/loggedInUser`,
     headers: requestHeaderParam ); 

我在 VS 代码中收到的错误消息如下

[ts] 类型参数 ' headers: GetLoggedInUserHeaderRequestParam; ' 不能分配给类型为 ' headers?: HttpHeaders | [标题:字符串]:字符串 |细绳[]; ;观察?:“身体”;参数?: H T...'。属性“标题”的类型不兼容。 类型“GetLoggedInUserHeaderRequestParam”不可分配给类型“HttpHeaders | [标题:字符串]:字符串 |细绳[]; '。 类型 'GetLoggedInUserHeaderRequestParam' 不可分配给类型 ' [header: string]: string |细绳[]; '。 “GetLoggedInUserHeaderRequestParam”类型中缺少索引签名。

请求参数类型如下

export interface GetLoggedInUserHeaderRequestParam 
  uid: string;
  PSID?: string;

HttpClient 声明如下。

HttpClient.get(url: string, options: 
   headers?: HttpHeaders | 
    [header: string]: string | string[];
   ;
   observe?: "body";
  params?: HttpParams | 
    [param: string]: string | string[];
  ;
  reportProgress?: boolean;
  responseType: "arraybuffer";
  withCredentials?: boolean;
): Observable<ArrayBuffer>

请帮忙!!!

注意我的问题不是如何使用 HttpHeaders,我知道如何使用 HttpHeaders,我的问题是如何直接使用 Object,如其声明类型之一中所述 [标题:字符串]:字符串 |细绳[]; 在 HttpClient 中

【问题讨论】:

【参考方案1】:

问题是GetLoggedInUserHeaderRequestParam 和 Headers 直接对象的签名不同

例如

  export interface GetLoggedInUserHeaderRequestParam 
      uid: string;
      PSID?: string;
    

仅接受带有 uid 和可选 PSID 的对象,而 Headers 直接对象

   
      [header: string]: string | string[];
   

表示它可以接受具有任意数量的字符串类型键和值作为字符串或字符串数​​组的对象。

关键区别在于您的接口强制 Typescript 接受具有确切键名的对象,其中标头对象可以接受任意数量的具有类型字符串的键,例如


  "uid" : "1234",
  "PSID" : "1223",
  "name" : "test",
  ..... 

你可以通过定义接口来解决问题

interface GetLoggedInUserHeaderRequestParam 
    [name: string] : string | string[];


并调用HTTP方法为

let header: GetLoggedInUserHeaderRequestParam  = 
  "uid" : "1234",
  "PSID" : "1234"


getLoggedInUser(header);

【讨论】:

【参考方案2】:

应该是这样的:

headers 是 HTTP 标头。你提供的是数据

getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): Observable<LoggedInUserResponse> 

     let headers = new HttpHeaders();
     headers = headers.set('Content-Type', 'application/json');

   return this.http.get<LoggedInUserResponse>(`$environment.apiBaseUrl/auth/loggedInUser`, JSON.stringify(requestHeaderParam),
     headers: headers ); 

参数:

    import HttpParams from "@angular/common/http";

getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): Observable<LoggedInUserResponse> 

    const params = new HttpParams()
        .set('uid', requestHeaderParam.uid)
        .set('PSID', requestHeaderParam.PSID);

     return this.http.get<LoggedInUserResponse>(`$environment.apiBaseUrl/auth/loggedInUser`, 
        params:params); 

【讨论】:

那里有很多很棒的资源:@​​987654321@ 如果您愿意,可以了解所有相关信息。如果您真的想使用参数,那么您可以在那里找到如何使用它 感谢您的帮助!我了解如何使用 HttpHeaders,我的问题是如何使用 Object 作为其声明类型之一中提到的标头 [header: string]: string |细绳[]; 在 HttpClient 中【参考方案3】:

您试图将符合 GetLoggedInUserHeaderRequestParam 的对象作为 headersparams 传递给 HttpClient.getoptions,但这不是类型安全的,这就是 TypeScript 阻止您这样做的原因。

看,你声明了参数requestHeaderParam: GetLoggedInUserHeaderRequestParam。这表示requestHeaderParam:

    必须有一个uid 字段,即string。 可能有PSID 字段,它是string

但它没有说明任何额外的字段。一个对象可以满足接口并包含其他字段。其中一些其他字段可能不是string。这是一个插图。为了说明我在说什么,我截取了你的部分代码并删除了一些不重要的东西:

interface GetLoggedInUserHeaderRequestParam 
    uid: string;
    PSID?: string;


function getLoggedInUser(requestHeaderParam: GetLoggedInUserHeaderRequestParam): void 


const params = 
    uid: "1",
    rogue: getLoggedInUser, // Let's pass a function!
;

// This compiles just fine despite params having an extra field.
getLoggedInUser(params);

// This does not compile.
getLoggedInUser(
    uid: "1",
    rogue: getLoggedInUser,  // Let's pass a function!
)

我放了两个调用getLoggedInUser 的例子,第一个编译得很好。第二个没有。第二个示例可能会导致一些 TypeScript 用户认为,如果未在接口上定义字段,则该字段是不允许的,但通常并非如此。当 TypeScript 将接口 应用于对象文字 时,它会拒绝那些未在接口中定义的字段,但此行为仅适用于 对象文字。 (并且它可以被类型断言覆盖。)我在上面展示的对getLoggedInUser 的第一次调用向您展示了一般情况下发生的情况:一个对象可以满足一个接口,但还有其他字段。

那么为什么会出现问题呢? headers [header: string]: string | string[] 声明告诉您 HttpClient.get 想要一个对象,其键是字符串,值是字符串或字符串数​​组。 值不能是任何其他类型。 正如我们刚刚看到的,不能保证requestHeaderParam 不会有违反此要求的字段。它将有一个 uid 字段,它是一个字符串,它可能有一个 PSID 字段,如果存在,它是一个字符串,但它可能有其他不是字符串或数组的字段的字符串。所以编译器正确地引发了一个错误。如果您尝试使用params,同样的逻辑也适用。

需要什么解决方案主要取决于使用您的代码的上下文。一个最小的解决方案是简单地做一个类型断言:

this.http.get(...,  headers: requestHeaderParam as unknown as Record<string, string>)

您必须先通过unknown(或any),因为这些类型完全不兼容。 请注意,如果您只使用类型断言,则如果传递的值不是stringstring[],则没有任何保护。 数据只会传递给HttpClient.get,而您'会出现错误或未定义的行为。

或者您可以在测试对象没有任何附加字段之后进行类型断言。

或者它可能requestHeaderParam 设置为特定类的对象更有意义,它a)强制可以设置哪些字段,并且b)具有返回普通JS对象的方法符合headers 的要求。

【讨论】:

感谢您的详细解答。我帮助我了解了很多关于对象声明的知识。

以上是关于理解复杂的类型脚本类型声明的主要内容,如果未能解决你的问题,请参考以下文章

转:如何理解c和c ++的复杂类型声明

正确理解c和c ++的复杂类型声明

shell脚本编程之变量简介及脚本执行过程

bash脚本之一(变量+数组)

shell 简单脚本编程

LinuxBash脚本