Понимание сложного объявления типа сценария типа - PullRequest
3 голосов
/ 18 апреля 2019

Вопрос о том, как использовать тип объекта для отправки заголовков, кроме HttpHeaders, предоставленных в объявлении HTTPClient.

При использовании 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 | { [заголовок: строка]: строка | Строка []; }; наблюдать ?: "тело"; Титулы ?: Ht ... '. Типы свойств «заголовки» несовместимы. Тип 'GetLoggedInUserHeaderRequestParam' нельзя назначить типу 'HttpHeaders | {[header: string]: строка | Строка []; }». Тип '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, Мой вопрос состоит в том, как использовать объект напрямую, как упомянуто в одном из его типов объявления {[ заголовок: строка]: строка | Строка []; } в HttpClient

Ответы [ 3 ]

3 голосов
/ 26 апреля 2019

Проблема в том, что разница в сигнатуре GetLoggedInUserHeaderRequestParam и прямого объекта Headers

, например,

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

, допускает только объект с uid и необязательным PSID wherease Headers direct object

   {
      [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 голосов
/ 25 апреля 2019

Вы пытаетесь передать объект, соответствующий GetLoggedInUserHeaderRequestParam как headers или params как HttpClient.get options, но это небезопасно, и поэтому TypeScript мешает вам это сделать.

Видите, вы объявляете параметр requestHeaderParam: GetLoggedInUserHeaderRequestParam.Это говорит о том, что requestHeaderParam:

  1. должно иметь поле uid, которое является string.
  2. может иметь поле 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, который я показываю выше, показывает вам, что происходит в общем случае: объект может удовлетворять интерфейсу и при этом иметь дополнительные поля.

Итакпочему это проблема?Объявление { [header: string]: string | string[] } для headers говорит вам, что HttpClient.get хочет объект, ключи которого являются строками, а значения являются либо строками, либо массивами строк. Значения не могут быть любого другого типа. Как мы только что видели, нет гарантии, что requestHeaderParam не будет иметь полей, которые нарушают это требование.У него будет поле uid, которое является строкой, оно может иметь поле PSID, которое, если оно есть, является строкой, но может также иметь другие поля, которые не являются строками или массивамистроки.Так что компилятор по праву выдает ошибку.Та же самая логика применима, если вы пытаетесь использовать params.

То, что требуется для решения, зависит в основном от контекста, в котором используется ваш код.Минимальным решением было бы просто сделать утверждение типа:

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

Сначала вам нужно пройти через unknown (или any), потому что типы полностью несовместимы. Обратите внимание: если вы используете только утверждение типа, защита не будет предоставлена, если будет передано значение, отличное от string или string[]. Данные будут просто переданы на HttpClient.get, и вы получитеполучить ошибку или неопределенное поведение.

Или вы можете сделать утверждение типа после проверки, что у объекта нет никаких дополнительных полей.

Или это может имеет больше смысла делать requestHeaderParam объектом определенного класса, который а) устанавливает, какие поля могут быть установлены, и б) имеет метод, который возвращает простой объект JS, который соответствует тому, что требует headers.

2 голосов
/ 18 апреля 2019

должно быть что-то вроде этого:

заголовки - это заголовки 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}); 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...