Как использовать выборку в typescript

Я использую window.fetch в Typescript, но я не могу отнести ответ непосредственно к своему пользовательскому типу:

Я взламываю свой путь вокруг этого, отбрасывая результат Promise промежуточной переменной "any".

Каким будет правильный способ сделать это?

import { Actor } from './models/actor';

fetch(`http://swapi.co/api/people/1/`)
      .then(res => res.json())
      .then(res => {
          // this is not allowed
          // let a:Actor = <Actor>res;

          // I use an intermediate variable a to get around this...
          let a:any = res; 
          let b:Actor = <Actor>a;
      })

Ответ 1

Далее следуют несколько примеров, от базового до добавления преобразований после обработки запроса и/или ошибки:

Основные:

// Implementation code where T is the returned data shape
function api<T>(url: string): Promise<T> {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(response.statusText)
      }
      return response.json<T>()
    })

}

// Consumer
api<{ title: string; message: string }>('v1/posts/1')
  .then(({ title, message }) => {
    console.log(title, message)
  })
  .catch(error => {
    /* show error message */
  })

Преобразование данных:

Часто вам может потребоваться внести некоторые изменения в данные, прежде чем они будут переданы потребителю, например, развернув атрибут данных верхнего уровня. Это прямо:

function api<T>(url: string): Promise<T> {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(response.statusText)
      }
      return response.json<{ data: T }>()
    })
    .then(data => { /* <-- data inferred as { data: T }*/
      return data.data
    })
}

// Consumer - consumer remains the same
api<{ title: string; message: string }>('v1/posts/1')
  .then(({ title, message }) => {
    console.log(title, message)
  })
  .catch(error => {
    /* show error message */
  })

Обработка ошибок:

Я бы сказал, что вы не должны прямо ловить ошибки непосредственно в этом сервисе, просто позволяя ему пузыриться, но если вам нужно, вы можете сделать следующее:

function api<T>(url: string): Promise<T> {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(response.statusText)
      }
      return response.json<{ data: T }>()
    })
    .then(data => {
      return data.data
    })
    .catch((error: Error) => {
      externalErrorLogging.error(error) /* <-- made up logging service */
      throw error /* <-- rethrow the error so consumer can still catch it */
    })
}

// Consumer - consumer remains the same
api<{ title: string; message: string }>('v1/posts/1')
  .then(({ title, message }) => {
    console.log(title, message)
  })
  .catch(error => {
    /* show error message */
  })

редактировать

Со времени написания этого ответа произошли некоторые изменения. Как упоминалось в комментариях, response.json<T> больше не действителен. Не уверен, не мог найти, где это было удалено.

Для более поздних выпусков вы можете сделать:

// Standard variation
function api<T>(url: string): Promise<T> {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(response.statusText)
      }
      return response.json() as Promise<T>
    })
}


// For the "unwrapping" variation

function api<T>(url: string): Promise<T> {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(response.statusText)
      }
      return response.json() as Promise<{ data: T }>
    })
    .then(data => {
        return data.data
    })
}

Ответ 2

Если вы посмотрите @types/ node -fetch, вы увидите определение тела

export class Body {
    bodyUsed: boolean;
    body: NodeJS.ReadableStream;
    json(): Promise<any>;
    json<T>(): Promise<T>;
    text(): Promise<string>;
    buffer(): Promise<Buffer>;
}

Это означает, что вы можете использовать дженерики для достижения желаемого. Я не тестировал этот код, но он выглядит примерно так:

import { Actor } from './models/actor';

fetch(`http://swapi.co/api/people/1/`)
      .then(res => res.json<Actor>())
      .then(res => {
          let b:Actor = res;
      });