1. ホーム
  2. typescript

[解決済み] TypeScriptでfetchを使う方法

2022-03-08 09:54:45

質問

を使っています。 ウィンドウズ・フェッチ を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;
      })

解決方法は?

基本的なものから、リクエストやエラー処理の後に変換を追加するものまで、いくつかの例を以下に示します。

基本的なことです。

// 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
    })
}