ホームページ ウェブフロントエンド jsチュートリアル TypeScript を使用して型を蓄積する方法: 可能なすべての fetch() 結果を入力する

TypeScript を使用して型を蓄積する方法: 可能なすべての fetch() 結果を入力する

Dec 06, 2024 pm 12:40 PM

How to Use TypeScript to Accumulate Types: Typing ALL possible fetch() Results

TypeScript と Svelte (私たち皆が嫌う JavaScript と React でした) でアプリケーションを (チームと一緒に) 書き直し始めたとき、次の問題に直面しました。

HTTP 応答の可能なすべての本文を安全に入力するにはどうすればよいですか?

これを聞いてピンときますか?そうでないなら、あなたもおそらく「そのうちの一人」でしょう、ふふ。この図をよりよく理解するために、少し脱線しましょう。

この地域が未開拓のように見える理由

HTTP 応答の「考えられるすべての本体」については誰も気にしていないようです。これは、このために既に作成されたもの (まあ、おそらく ts-fetch) が見つからなかったためです。なぜそうなるのか、私の論理をここで簡単に説明しましょう。

人々は次のいずれかの理由で誰も気にしません:

  1. ハッピー パスのみに注意してください: HTTP ステータス コードが 2xx の場合の応答本文。

  2. 他の場所に手動で入力します。

#1 については、そうです、開発者 (特に経験の浅い開発者) は、HTTP リクエストが失敗する可能性があること、失敗した応答に含まれる情報が通常の応答とはまったく異なる可能性が高いことを忘れていると思います。

#2 では、ky や axios などの人気のある NPM パッケージで見つかった大きな問題を掘り下げてみましょう。

データ取得パッケージの問題

私が知る限り、人々は ky や axios のようなパッケージを好みます。その「機能」の 1 つは、OK 以外の HTTP ステータス コードでエラーをスローすることです。いつからOKになったんですか?以来。しかし、どうやら人々はこれを理解していないようです。人々は満足しており、OK 以外の応答でエラーが発生することに満足しています。

人々はキャッチするときに、OK ではないボディを入力すると思います。何という混乱、何というコードの匂い!

これはコードの臭いです。これは、try..catch ブロックを分岐ステートメントとして効果的に使用しているためであり、try..catch は分岐ステートメントであることを意図したものではありません。

しかし、分岐は try..catch で自然に発生するという私に反論があったとしても、これが依然として問題である大きな理由はもう 1 つあります。エラーがスローされると、ランタイムは呼び出しスタックを巻き戻す必要があるからです。これは、if または switch ステートメントによる通常の分岐よりも、CPU サイクルの点ではるかにコストがかかります。

これを知っていると、try..catch ブロックを誤用したためだけにパフォーマンスが低下することを正当化できますか?私はノーと言います。フロントエンドの世界がこれに完全に満足しているように見える理由は 1 つも思いつきません。

私の推論の流れを説明したので、本題に戻りましょう。

問題の詳細

HTTP 応答には、ステータス コードに応じて異なる情報が含まれる場合があります。たとえば、PATCH HTTP リクエストを受信する api/todos/:id などの todo エンドポイントは、レスポンスのステータス コードが 200 の場合、レスポンスのステータス コードが 400 の場合とは異なる本文のレスポンスを返す場合があります。

例を見てみましょう:

// For the 200 response, a copy of the updated object:
{
    "id": 123,
    "text": "The updated text"
}

// For the 400 response, a list of validation errors:
{
    "errors": [
        "The updated text exceeds the maximum allowed number of characters."
    ]
}
ログイン後にコピー
ログイン後にコピー

これを念頭に置いて、問題ステートメントに戻ります。この PATCH リクエストを実行する関数をどのように入力すればよいでしょうか。TypeScript は、記述中の HTTP ステータス コードに応じて、どの本体を処理しているのかを教えてくれます。コード?答え: 型を蓄積するには、流暢な構文 (ビルダー構文、連鎖構文) を使用します。

ソリューションの構築

前の型を基にして型を定義することから始めましょう:

export type AccumType<T, NewT> = T | NewT;
ログイン後にコピー
ログイン後にコピー

非常に簡単: 型 T と NewT が与えられた場合、それらを結合して新しい型を形成します。この新しい型を AccumType<> で再度 T として使用すると、別の新しい型を蓄積できます。ただし、これを手作業で行うのは好ましくありません。ソリューションのもう 1 つの重要な要素である流暢な構文を紹介しましょう。

流暢な構文

メソッドが常に自分自身 (または自分自身のコピー) を返すクラス X のオブジェクトがあると、メソッド呼び出しを次々に連鎖させることができます。これは流暢な構文、または連鎖構文です。

これを行う簡単なクラスを書いてみましょう:

export class NamePending<T> {
    accumulate<NewT>() {
        return this as NamePending<AccumType<T, NewT>>;
    }
}

// Now you can use it like this:
const x = new NamePending<{ a: number; }>();  // x is of type NamePending<{ a: number; }>.
const y = x.accumulate<{ b: string; }>  // y is of type NamePending<{ a: number; } | { b: string; }>.
ログイン後にコピー
ログイン後にコピー

エウレカ!流暢な構文と、データ型を 1 つの型に蓄積し始めるために作成した型をうまく組み合わせることができました!

それが明らかでない場合は、目的のタイプが蓄積されるまで演習を続けることができます (x.accumulate().accumulate()…完了するまで)。

これはすべて良いことであり、素晴らしいことですが、この非常に単純なタイプでは、HTTP ステータス コードが対応するボディ タイプに関連付けられていません。

私たちが持っているものを磨き上げる

私たちが望んでいるのは、TypeScript の型絞り込み機能が作動するのに十分な情報を TypeScript に提供することです。これを行うには、元の問題に関連するコードを取得する必要があります (HTTP 応答の本文を per 形式で入力します)。 -ステータスコードベース)。

まず、AccumType の名前を変更して進化させます。以下のコードは、反復の進行を示しています:

// Iteration 1.
export type FetchResult<T, NewT> = T | NewT;
// Iteration 2.
export type FetchResponse<TStatus extends number, TBody> = {
    ok: boolean;
    status: TStatus;
    statusText: string;
    body: TBody
};

export type FetchResult<T, TStatus extends number, NewT> = 
    T | FetchResponse<TStatus, NewT>; //Makes sense to rename NewT to TBody.
ログイン後にコピー
ログイン後にコピー

この時点で、私は次のことに気づきました。ステータス コードは有限です。ステータス コードを検索して型を定義し、それらの型を使用して型パラメータ TStatus を制限することができます (そして実際にそうしました)。

// Iteration 3.
export type OkStatusCode = 200 | 201 | 202 | ...;
export type ClientErrorStatusCode = 400 | 401 | 403 | ...;
export type ServerErrorStatusCode = 500 | 501 | 502 | ...;
export type StatusCode = OkStatusCode | ClientErrorStatusCode | ServerErrorStatusCode;
export type NonOkStatusCode = Exclude<StatusCode, OkStatusCode>;

export type FetchResponse<TStatus extends StatusCode, TBody> = {
    ok: TStatus extends OkStatusCode ? true : false;
    status: TStatus;
    statusText: string;
    body: TBody
};

export type FetchResult<T, TStatus extends StatusCode, TBody> = 
    T | FetchResponse<TStatus, TBody>;
ログイン後にコピー
ログイン後にコピー

私たちは、ただただ美しい一連の型に到達しました。ok または status プロパティの条件に基づいて分岐する (if ステートメントを書く) ことによって、TypeScript の型絞り込み機能が作動します。信じられない場合は、クラス部分を作成して試してみましょう:

export class DrFetch<T> {
    for<TStatus extends StatusCode, TBody>() {
        return this as DrFetch<FetchResult<T, TStatus, TBody>>;
    }
}
ログイン後にコピー
ログイン後にコピー

これをテストしてみましょう:

// For the 200 response, a copy of the updated object:
{
    "id": 123,
    "text": "The updated text"
}

// For the 400 response, a list of validation errors:
{
    "errors": [
        "The updated text exceeds the maximum allowed number of characters."
    ]
}
ログイン後にコピー
ログイン後にコピー

型の絞り込みが、status プロパティの ok プロパティに基づいて、分岐時に本体の形状を正確に予測できる理由が明確になるはずです。

ただし、問題があります。クラスがインスタンス化されるときの最初の型付けは、上のコメント ブロックでマークされています。私は次のようにそれを解決しました:

export type AccumType<T, NewT> = T | NewT;
ログイン後にコピー
ログイン後にコピー

この小さな変更により、最初の入力作業が実質的に不要になり、現在は業務を開始しています!

これで、次のようなコードを作成できるようになり、Intellisense は 100% 正確になります。

export class NamePending<T> {
    accumulate<NewT>() {
        return this as NamePending<AccumType<T, NewT>>;
    }
}

// Now you can use it like this:
const x = new NamePending<{ a: number; }>();  // x is of type NamePending<{ a: number; }>.
const y = x.accumulate<{ b: string; }>  // y is of type NamePending<{ a: number; } | { b: string; }>.
ログイン後にコピー
ログイン後にコピー

型の絞り込みは、ok プロパティをクエリするときにも機能します。

気づかなかった方もいるかもしれませんが、エラーをスローしないことで、はるかに優れたコードを書くことができました。私の専門的な経験では、axios も ky も間違っており、同じことをしている他のフェッチ ヘルパーも間違っています。

結論

TypeScript は確かに楽しいです。 TypeScript と Fluent 構文を組み合わせることで、必要なだけ多くの型を蓄積できるため、何度もデバッグを繰り返すのではなく、初日からより正確で明確なコードを書くことができます。この手法は成功していることが証明されており、誰でも試すことができます。 dr-fetch をインストールしてテストします:

// Iteration 1.
export type FetchResult<T, NewT> = T | NewT;
// Iteration 2.
export type FetchResponse<TStatus extends number, TBody> = {
    ok: boolean;
    status: TStatus;
    statusText: string;
    body: TBody
};

export type FetchResult<T, TStatus extends number, NewT> = 
    T | FetchResponse<TStatus, NewT>; //Makes sense to rename NewT to TBody.
ログイン後にコピー
ログイン後にコピー

より複雑なパッケージ

また、廃止された .env ファイルと dotenv を完全に削除することを目的としたパッケージ、wj-config も作成しました。このパッケージも、ここで説明した TypeScript トリックを使用しますが、型を | ではなく & で結合します。試してみたい場合は、v3.0.0-beta.1 をインストールしてください。ただし、型指定ははるかに複雑です。 wj-config の後に dr-fetch を作成するのは、公園を散歩するようなものでした。

楽しいこと: そこにあるもの

フェッチ関連のパッケージに存在するエラーをいくつか見てみましょう。

同型フェッチ

README で次のことがわかります:

// Iteration 3.
export type OkStatusCode = 200 | 201 | 202 | ...;
export type ClientErrorStatusCode = 400 | 401 | 403 | ...;
export type ServerErrorStatusCode = 500 | 501 | 502 | ...;
export type StatusCode = OkStatusCode | ClientErrorStatusCode | ServerErrorStatusCode;
export type NonOkStatusCode = Exclude<StatusCode, OkStatusCode>;

export type FetchResponse<TStatus extends StatusCode, TBody> = {
    ok: TStatus extends OkStatusCode ? true : false;
    status: TStatus;
    statusText: string;
    body: TBody
};

export type FetchResult<T, TStatus extends StatusCode, TBody> = 
    T | FetchResponse<TStatus, TBody>;
ログイン後にコピー
ログイン後にコピー

「サーバーからの応答が悪い」??いいえ。 「サーバーはあなたのリクエストが間違っていると言います。」そう、投げる部分自体がひどいのです。

tsフェッチ

これは正しいアイデアを持っていますが、残念ながら、OK 応答と非 OK 応答 (最大 2 種類) しか入力できません。

きー

私が最も批判したパッケージの 1 つは、次の例を示しています。

export class DrFetch<T> {
    for<TStatus extends StatusCode, TBody>() {
        return this as DrFetch<FetchResult<T, TStatus, TBody>>;
    }
}
ログイン後にコピー
ログイン後にコピー

これは、非常に若い開発者が書くものです: まさに幸せな道。 README によれば、同等性は次のとおりです。

const x = new DrFetch<{}>(); // Ok, having to write an empty type is inconvenient.
const y = x
    .for<200, { a: string; }>()
    .for<400, { errors: string[]; }>()
    ;
/*
y's type:  DrFetch<{
    ok: true;
    status: 200;
    statusText: string;
    body: { a: string; };
}
| {
    ok: false;
    status: 400;
    statusText: string;
    body: { errors: string[]; };
}
| {} // <-------- WHAT IS THIS!!!???
>
*/
ログイン後にコピー

投げる部分はとてもひどいです。後で捕まえるように強制するために、なぜ投げることに分岐するのでしょうか?私にとってそれは全く意味がありません。エラーのテキストも誤解を招きます。これは「フェッチ エラー」ではありません。フェッチは機能しました。返事が来ましたね。あなたはそれが気に入らなかっただけです…それは幸せな道ではないからです。より適切な表現は、「HTTP リクエストが失敗しました:」です。失敗したのはリクエスト自体であり、取得操作ではありません。

以上がTypeScript を使用して型を蓄積する方法: 可能なすべての fetch() 結果を入力するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? Apr 04, 2025 pm 02:42 PM

フロントエンドのサーマルペーパーチケット印刷のためのよくある質問とソリューションフロントエンド開発におけるチケット印刷は、一般的な要件です。しかし、多くの開発者が実装しています...

javascriptの分解:それが何をするのか、なぜそれが重要なのか javascriptの分解:それが何をするのか、なぜそれが重要なのか Apr 09, 2025 am 12:07 AM

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は?
または:
Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は? または: Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Apr 04, 2025 pm 05:36 PM

この記事の視差スクロールと要素のアニメーション効果の実現に関する議論では、Shiseidoの公式ウェブサイト(https://www.shisido.co.co.jp/sb/wonderland/)と同様の達成方法について説明します。

JavaScriptの進化:現在の傾向と将来の見通し JavaScriptの進化:現在の傾向と将来の見通し Apr 10, 2025 am 09:33 AM

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

JavaScriptは学ぶのが難しいですか? JavaScriptは学ぶのが難しいですか? Apr 03, 2025 am 12:20 AM

JavaScriptを学ぶことは難しくありませんが、挑戦的です。 1)変数、データ型、関数などの基本概念を理解します。2)非同期プログラミングをマスターし、イベントループを通じて実装します。 3)DOM操作を使用し、非同期リクエストを処理することを約束します。 4)一般的な間違いを避け、デバッグテクニックを使用します。 5)パフォーマンスを最適化し、ベストプラクティスに従ってください。

JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? Apr 04, 2025 pm 05:09 PM

同じIDを持つ配列要素をJavaScriptの1つのオブジェクトにマージする方法は?データを処理するとき、私たちはしばしば同じIDを持つ必要性に遭遇します...

Zustand非同期操作:UseStoreが取得した最新の状態を確保する方法は? Zustand非同期操作:UseStoreが取得した最新の状態を確保する方法は? Apr 04, 2025 pm 02:09 PM

Zustand非同期操作のデータの更新問題。 Zustand State Management Libraryを使用する場合、非同期操作を不当にするデータ更新の問題に遭遇することがよくあります。 �...

See all articles