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 マスタースレーブの原理と構成の詳細

推薦する

VMware Workstation16 と Navicat リモート接続での Centos7 での MySQL8.0 インストール プロセス

目次1. CentOS7+MySQL8.0、yumソースインストール2. MySQLにログインしてパ...

Docker を使用して Microsoft Sql Server を展開するための詳細な手順

目次1 背景2 コンテナを作成する3 SAパスワードを変更する4 mssql のリンク5. コンテナ...

Linux には make コマンドがありません (make: *** ターゲットが指定されておらず、makefile または make コマンドのインストール方法が見つかりません)

知らせ! ! !この状況は、実際には仮想マシンのインストール中に回避できます。次回仮想マシンをテスト...

HTML の隠しフィールドの紹介と例

基本的な構文: <input type="hidden" name=&qu...

mysql 結合クエリ (左結合、右結合、内部結合)

1. MySQLの一般的な接続INNER JOIN (内部結合、または等価結合): 2 つのテーブ...

方言変換のためのApache Calciteコード

意味Calcite は、Sql を SqlNode に解析し、次に SqlNode を特定のデータベ...

MySQL の同時実行性の問題と解決策の分析

目次1. 背景2. テーブルロックによるクエリの遅延3. オンラインでテーブル構造を変更するとどのよ...

図を使ってWeb2.0とは何かを説明する

最近はWeb2.0という言葉をよく耳にしますが、Web2.0とは何でしょうか? Web 1.0 とど...

Nginx で同じドメイン名を持つ複数のプロジェクトを構成する方法

Nginx を使用して同じドメイン名で複数のプロジェクトを構成するには、次の 2 つの方法があります...

Linux manコマンドの具体的な使い方

01. コマンドの概要Linux には充実したヘルプ マニュアルが用意されています。コマンドのパラメ...

border-radius 値の設定に関する質問

問題記録今日はプログレスバーに似た小さなコンポーネントを完成させるつもりでした。プロトタイプは次のよ...

Nginx ロケーション設定(ロケーションのマッチング順序)の詳細な説明

ロケーションは「位置指定」を意味し、主にさまざまな位置指定のための URI に基づいています。これは...

JDBCデータベースリンクと関連メソッドのカプセル化の詳細な説明

JDBCデータベースリンクと関連メソッドのカプセル化の詳細な説明MySQL データベースを使用して、...

Vueはプラグインを使用して画像を比例してカットします

この記事では、プラグインを使用して画像の比例カットを実現するVueの具体的なコードを参考までに共有し...

eject を使用せずに create-react-app の設定を変更する方法

1. イジェクトが推奨されないのはなぜですか? 1. eject を実行した後、どのような変化があり...