TSオブジェクトのスプレッド演算子とレスト演算子の詳細な説明

TSオブジェクトのスプレッド演算子とレスト演算子の詳細な説明

概要

TypeScript 2.1 では、ES2018 で標準化されているオブジェクト スプレッド演算子と残りのプロパティ提案のサポートが追加されました。 rest 属性と spread 属性は型安全に使用できます。

オブジェクトの残り属性

3つのプロパティを持つ単純なリテラルオブジェクトが定義されていると仮定します。

const マリウス = {
  名前: 「マリウス・シュルツ」
  ウェブサイト: "https://mariusschulz.com/",
  twitterハンドル: "@mariusschulz"
};

ES6 の構造化構文を使用すると、対応するプロパティの値を保持するための複数のローカル変数を作成できます。 TypeScript は各変数の型を正しく推測します。

const { name, website, twitterHandle } = marius;

name; // 文字列型
website; // 文字列型
twitterHandle; // 文字列型

これらはすべて正しいですが、新しいものではありません。関心のあるプロパティのセットを抽出するだけでなく、... 構文を使用して残りのすべてのプロパティを REST 要素に収集することもできます。

const { twitterHandle, ...rest } = marius;

twitterHandle; // 文字列型
rest; // タイプ { name: string; website: string; }

TypeScript は、結果として得られるローカル変数の正しい型を決定します。 twitterHandle 変数はプレーンな文字列ですが、rest 変数は残りの 2 つのプロパティが構造化されていないオブジェクトです。

オブジェクトの拡張プロパティ

fetch() API を使用して HTTP リクエストを実行したいとします。これは、URL と、リクエストのカスタム設定を含むオプション オブジェクトの 2 つの引数を受け入れます。

アプリケーションでは、fetch() の呼び出しをカプセル化し、デフォルトのオプションと、特定のリクエストに対してそれらのオプションをオーバーライドする特定の設定の両方を提供できます。これらの構成項目は次のようになります。

定数デフォルトオプション = {
  メソッド: "GET",
  資格情報: 「同一オリジン」
};

const リクエストオプション = {
  メソッド: "POST",
  リダイレクト: 「フォロー」
};

オブジェクト拡張を使用すると、2 つのオブジェクトを新しいオブジェクトにマージし、fetch() メソッドに渡すことができます。

// タイプ { method: string; redirect: string; credentials: string; }
定数オプション = {
  ...デフォルトオプション、
  ...リクエストオプション
};

オブジェクト拡張プロパティは、新しいオブジェクトを作成し、defaultOptions 内のすべてのプロパティ値をコピーしてから、requestOptions 内のすべてのプロパティ値を左から右の順にコピーします。最終結果は次のようになります。

console.log(オプション);
// {
// メソッド: "POST",
// 資格情報: "同一オリジン",
// リダイレクト: "follow"
// }

割り当ての順序が重要であることに注意してください。両方のオブジェクトにプロパティが存在する場合、後で割り当てられたプロパティが、前に割り当てられたプロパティに置き換えられます。

もちろん、TypeScript はこの順序を理解します。したがって、複数の拡張オブジェクトが同じキーを持つプロパティを定義する場合、結果のオブジェクト内のそのプロパティの型は、以前に割り当てられたプロパティをオーバーライドするため、最後に割り当てられたプロパティの型になります。

obj1 は 42 です。
const obj2 = { プロパティ: "Hello World" };

const result1 = { ...obj1, ...obj2 }; // 型 { prop: string }
const result2 = { ...obj2, ...obj1 }; // 型 { prop: number }

オブジェクトの浅いコピーを作成する

オブジェクトの拡散を使用すると、オブジェクトの浅いコピーを作成できます。新しいオブジェクトを作成し、すべてのプロパティをコピーして、既存の ToDo 項目から新しい ToDo 項目を作成したいとします。これは、オブジェクトを使用すると簡単に実行できます。

定数todo = {
  テキスト:「花に水をやる」
  完了: 偽、
  タグ: ["庭"]
};

const shallowCopy = { ...todo };

実際には、すべてのプロパティ値がコピーされた新しいオブジェクトが取得されます。

console.log(todo === 浅いコピー);
// 間違い

console.log(shallowCopy);
// {
// テキスト: "花に水をやる",
// 完了: false、
// タグ: ["庭"]
// }

これで、元の ToDo 項目を変更せずにテキスト プロパティを変更できます。

hallowCopy.text = "芝を刈る";

console.log(浅いコピー);
// {
// テキスト: "芝を刈る",
// 完了: false、
// タグ: ["庭"]
// }

コンソールにログ出力します。
// {
// テキスト: "花に水をやる",
// 完了: false、
// タグ: ["庭"]
// }

ただし、新しい ToDo 項目は最初の項目と同じタグ配列を参照します。浅いコピーなので、配列を変更すると両方のtodoに影響します。

shallowCopy.tags.push("週末");

console.log(浅いコピー);
// {
// テキスト: "芝を刈る",
// 完了: false、
// タグ: ["庭", "週末"]
// }

コンソールにログ出力します。
// {
// テキスト: "花に水をやる",
// 完了: false、
// タグ: ["庭", "週末"]
// }

シリアル化されたオブジェクトのディープコピーを作成する場合は、jsON.parse(jsON.stringify(obj)) または object.assign() などの他のメソッドの使用を検討してください。オブジェクト拡張はプロパティ値のみをコピーするため、1 つの値が別のオブジェクトへの参照である場合、予期しない動作が発生する可能性があります。

keyof と lookup 型

JS は非常に動的な言語です。静的型システムで特定の操作のセマンティクスをキャプチャするのは難しい場合があります。単純な prop 関数を例に挙げます。

関数 prop(obj, key) {
  obj[キー]を返します。
}

オブジェクトとキーを受け取り、対応するプロパティの値を返します。オブジェクトの異なるプロパティは完全に異なる型を持つ可能性があり、obj がどのように見えるかさえわかりません。

では、この関数を TypeScript でどのように記述するのでしょうか?まずこれを試してください:

これら 2 つの型注釈では、obj はオブジェクトであり、key は文字列である必要があります。これで、両方のパラメータの可能な値のセットが制限されました。ただし、TS は戻り値の型を any として推論します。

定数todo = {
  id: 1,
  テキスト:「牛乳を買う」
  期限: 新しい日付(2016年11月31日)
};

const id = prop(todo, "id"); // 任意
const text = prop(todo, "text"); // 任意
const due = prop(todo, "due"); // 任意

詳細情報がないと、TypeScript は key パラメータに渡される値がわからないため、prop 関数のより具体的な戻り値の型を推測できません。これを実現するには、より多くの型情報を提供する必要があります。

keyof 演算子

プロパティ名をパラメータとして受け取る API は JS では非常に一般的ですが、これまではそれらの API に表示される型関係を表現する方法がありませんでした。

TypeScript 2.1 では、keyof 演算子が追加されました。インデックス タイプ クエリまたは keyof を入力します。インデックス タイプ クエリ keyof T によって生成されるタイプは、T の属性名です。次のような Todo インターフェースを定義したとします。

インターフェースTodo {
  id: 番号;
  テキスト: 文字列;
  期日;
}

keyof 演算子を Todo 型に適用すると、そのすべてのプロパティ キーの型 (文字列リテラル型の結合) を取得できます。

type TodoKeys = keyof Todo; // "id" | "text" | "due"

もちろん、keyof を使用する代わりに、ユニオン タイプ「id」|「text」|「due」を手動で記述することもできますが、これは面倒で、エラーが発生しやすく、保守が困難です。また、汎用的なソリューションではなく、Todo タイプ固有のソリューションである必要があります。

インデックス型クエリ

keyof を導入することで、 prop 関数の型注釈を改善できるようになりました。任意の文字列をキーパラメータとして受け入れることはもう望ましくありません。代わりに、渡されたオブジェクトの型にパラメータ キーが実際に存在する必要があります。

関数 prop<T, K は keyof T>(obj: T, key: K) を拡張します {
  obj[キー]を返す
}

TypeScript は、 prop 関数の戻り値の型を T[K] と推論するようになりました。これは、インデックス付き型検索または型検索と呼ばれます。これは、タイプ T の属性 K のタイプを表します。ここで、prop メソッドを介して以下の todo の 3 つのプロパティにアクセスすると、各プロパティの型は正しいものになります。

定数todo = {
  id: 1,
  テキスト:「牛乳を買う」
  期限: 新しい日付(2016年11月31日)
};

const id = prop(todo, "id"); // 数値
const text = prop(todo, "text"); // 文字列
const due = prop(todo, "due"); // 日付

さて、todo オブジェクトに存在しないキーを渡すとどうなるでしょうか?

コンパイラはエラーを出力しますが、これは良いことです。存在しないプロパティを読み取ろうとするのを防ぐことができるからです。

別の実際の例として、TypeScript コンパイラとともに配布される lib.es2017.object.d.ts 型宣言ファイルの Object.entries() メソッドを見てみましょう。

インターフェースObjectConstructor {
  // ...
  エントリ<T は { [key: string]: any } を拡張し、 K は keyof T>(o: T): [keyof T, T[K]][] を拡張します。
  // ...
}

エントリ メソッドはタプルの配列を返します。各タプルには属性キーと対応する値が含まれています。確かに、戻り値の型には角括弧がたくさんありますが、私たちは常に型の安全性を追求しています。

以上がTSオブジェクトスプレッド演算子とレスト演算子の詳しい説明です。TSオブジェクトスプレッド演算子とレスト演算子の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • JavaScript スプレッド演算子の使用例のまとめ [ES6 ベース]
  • ES6拡張演算子の理解と使用シナリオ
  • ES6スプレッド演算子の使用例
  • ES6拡張演算子の使い方と注意点を詳しく解説
  • ES6 拡張演算子と残り演算子の使用例の分析
  • ES6 配列拡張演算子操作例の分析
  • JS ES の新機能: 拡張演算子の紹介

<<:  Windows Server 2019 で NAS を構成する方法

>>:  MySQL マスタースレーブの原理と構成の詳細

推薦する

KVM ベースの SRIOV パススルー構成とパフォーマンス テストの詳細な説明

SRIOVの導入、VFパススルー構成、パケット転送速度性能テスト目次1. SRIOVの紹介2. 環境...

MySQLからElasticsearchにデータを同期する方法の詳細な説明

目次1. 同期の原理2. ログスタッシュ入力JDBC 3. go-mysql-elasticsear...

MySQL で 1 つのテーブルのフィールドを使用して別のテーブルのフィールドを更新する方法

1. 1列を変更する 学生の更新、都市c s.city_name = c.name を設定します こ...

Dockerにおけるオーバーレイネットワークの詳細な説明

Docker 公式ドキュメントからの翻訳、原文: https://docs.docker.com/n...

MySQL テーブルの読み取り、書き込み、インデックス作成、その他の操作の SQL ステートメントの効率最適化の問題を分析します。

前回は、Explain 実行プランの表示、インデックスの分析など、MySQL での SQL クエリの...

MySQL 文字列分割の例 (区切り文字なしの文字列抽出)

区切り文字なしの文字列抽出質問の要件データベース内のフィールド値:実装効果: 1行のデータを複数行に...

MySQL コード実行構造例の分析 [シーケンス、分岐、ループ構造]

この記事では、例を使用して MySQL コード実行構造について説明します。ご参考までに、詳細は以下の...

MySQL バッチ挿入ループの詳細なサンプルコード

背景数日前、MySql でページングを行っていたときに、ページングに制限 0,10 を使用するとデー...

JSはプログレスバーをドラッグして要素の透明度を変更することを実装しています

今日ご紹介したいのは、ネイティブ JS を使用してプログレス バーをドラッグし、要素の透明度を変更す...

Keepalived は Nginx の負荷分散と高可用性のサンプル コードを実装します

第1章: keepalivedの紹介VRRP プロトコルの目的は、静的ルーティングの単一点障害問題を...

Linux でパスワードの有効期限を表示および設定する方法

適切な設定を行うことで、Linux ユーザーにパスワードを定期的に変更させることができます。パスワー...

MySQL Index Pushdown (ICP) とは何かを理解するための記事

目次1. はじめに2. 原則III. 実践3.1 インデックスプッシュダウンを使用しない3.2 イン...

MYSQL 演算子の概要

目次1. 算術演算子2. 比較演算子3. 論理演算子4. ビット演算子5. 演算子の優先順位1. 算...

SpringBoot と Docker の統合の詳細なプロセス

目次1. デモプロジェクト1.1 インターフェースの準備1.2 構成の準備2. Dockerがリモー...

前後の秒、分、時間、日数を取得するMySQLデータベース

現在の時刻を取得します: current_timestamp を選択します。出力: 2016-06-...