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

推薦する

Docker可視化管理ツールであるDocker UIの使用

1. DockerUIの紹介DockerUI は Docker API をベースとしており、Dock...

js で 0ms 遅延タイマーを実装するいくつかの方法

目次キューマイクロタスク非同期/待機メッセージチャネルやっと付録ここ二日間、「タイムリーな setT...

Windows 7 で Python 3.4 を使って MySQL データベースを使用する

Python 3.4でMySQLデータベースを使用する詳細なプロセスは次のとおりです。 Window...

クラウドサーバーはBaotaを使用してPython環境を構築し、Djangoプログラムを実行します。

目次PagodaをインストールするPythonランタイム環境を構成するPythonをインストールする...

Node.jsはSTMPプロトコルとEWSプロトコルに基づいてメールを送信します

目次1 STMP プロトコルに基づいてメールを送信する Node.js メソッド2 MS Excha...

Alpine Dockerイメージフォント問題解決操作

1. フォントを実行し、フォント フォルダーを開いて、使用するフォント ファイルを見つけます。 2....

jQueryはドロップダウンメニューのスライド効果を実現します

Web ページを作成するときに、クールでスムーズなドロップダウン メニューが必要になることがあります...

js のプロトタイプ、プロトタイプ オブジェクト、プロトタイプ チェーンの包括的な分析

目次プロトタイプを理解するプロトタイプオブジェクトを理解するインスタンスプロパティとプロトタイププロ...

Firefox で Webdings フォントをサポートする方法

Firefox、Opera、その他のブラウザは Webdings フォントをサポートしていません。回...

CSS3 3Dクールキューブ変形アニメーションの実装

私はコーディングが大好きです。コーディングすると幸せになります!みなさんこんにちは、Counterで...

MySQL5.7.03 上位バージョンから MySQL 5.7.17 への置き換えインストール プロセスと見つかった問題の解決策

1. インストール方法は? 1. [実行] -> [cmd] と入力して、小さな黒いウィンドウ...

CSSでプロセスナビゲーション効果を実現する(3つの方法)

CSS によりプロセスナビゲーション効果を実現します。具体的な内容は以下のとおりです。 ::tip...

Ubuntu 20.04 では、隠し録音ノイズ低減機能が有効になります (推奨)

最近、 Ubuntu 20.04でkazamを使用して録音しているときに、問題が見つかりました。シス...

html+vue+element-ui のスムーズさを 1 分で体験

テクノロジーファンHTMLウェブページ、知っておくべきYouyou が開発した vue フロントエン...

Vue3 ベースのフルスクリーン ドラッグ アップロード コンポーネント

この記事は主に、みんなで共有できるVue3ベースのフルスクリーンドラッグアップロードコンポーネントを...