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

推薦する

JS の FileReader を介して .txt ファイルの内容を取得する方法

目次JSはFileReaderを通じて.txtファイルの内容を取得します。 .txtファイルの読み取...

MySQL接続クエリにおけるととwhereの違いの簡単な分析

1. テーブルを作成する テーブル「学生」を作成( `id` int(11) NULLではない、 `...

MySQL 5.7.13 のインストールと設定方法の Mac でのグラフィック チュートリアル

MySQL 5.7.13 Mac用インストールチュートリアル、非常に詳細で、以下のように記録されてい...

Tomcatを自動的に開始するサービスとして設定するにはどうすればいいでしょうか?最も簡単な方法

Tomcat が自動的にサービスを開始するように設定します。最近、問題が発生しました。サーバー上のプ...

Zabbix が MySQL のマスター/スレーブ状態を監視する方法の詳細な説明

MySQLマスタースレーブを設定した後、スレーブの状態が正常かどうかわからないことが多く、例外が発生...

MySQL で主キーと ROWID を使用する際の落とし穴の概要

序文MySQL の rowid の概念については聞いたことがあるかもしれませんが、テストや実践が難し...

Vue はトークンを取得してトークン ログインのサンプル コードを実装します

ログイン認証にトークンを使用する考え方は次のとおりです。 1. 初めてログインする場合、フロントエン...

Linux シェル環境での Zabbix API の使用

Linux シェル環境で直接呼び出すことができます。公式 Web サイトによると、Zabbix のデ...

Firefox で英語の文字が折り返されない問題の解決方法

テキストのレイアウトには、言語に応じていくつかの書式設定要件があります。たとえば、簡体字中国語では、...

Nodejs と Socket.IO を組み合わせて Websocket の即時通信を実現

目次WebSocketを使用する理由ソケット.ioオープンソースプロジェクト効果プレビューアプリイン...

Linux で実行中のバックグラウンド プログラムを表示および終了する方法

Linux タスク管理 - バックグラウンド実行と終了fg、bg、ジョブ、&、ctrl + ...

CSS3 カテゴリメニュー効果

CSS3 カテゴリ メニューの効果は次のとおりです。 html <html> <ヘ...

JavaScriptのループの違いについての詳細な説明

目次序文列挙可能なプロパティ反復可能なオブジェクトforEachメソッドとmapメソッドチェーン呼び...

Linuxはnode.jsを完全に削除し、yumコマンドで再インストールします。

最初のステップ組み込みのパッケージ管理機能で一度削除する yum 削除 nodejs npm -y ...

負荷分散と動的・静的分離を実現するNginx+Tomcatの原理の分析

1. Nginx ロードバランシングの実装原理1. Nginxはリバースプロキシを通じて負荷分散を実...