TypeScript の条件型に関する詳細な読書と実践記録

TypeScript の条件型に関する詳細な読書と実践記録

ほとんどのプログラムでは、入力に基づいて決定を下す必要があります。 TypeScript も例外ではなく、条件型を使用して入力型と出力型の関係を記述できます。

条件判断に使用される

条件判断を表現するために extends を使用する場合、次のルールをまとめることができます。

extends の両側の型が同じである場合、次の例に示すように、extends は意味的には === として理解できます。

type result1 = 'a' extends 'abc' ? true : false // false
type result2 = 123 extends 1 ? true : false // false

extends の右側の型に extends の左側の型が含まれている場合 (つまり、狭い型が広い型を拡張している場合)、結果は true になります。それ以外の場合は false になります。次の例を参照してください。

type result3 = string extends string | number ? true : false // true

オブジェクトに extends を適用すると、オブジェクトに指定されるキーが増えるほど、その型定義の範囲は狭くなります。次の例を参照してください。

type result4 = { a: true, b: false } extends { a: true } ? true : false // true

ジェネリック型での条件型の使用

次のデモ型定義を検討してください。

型 Demo<T, U> = T は U を拡張しますか? 決して: T

条件判断で使用される extends と組み合わせると、 'a' | 'b' | 'c' extends 'a' は false であることがわかります。したがって、 Demo<'a' | 'b' | 'c', 'a'> の結果は 'a' | 'b' | 'c'? になります。
公式ウェブサイトを確認してください。そこには次のように記載されています。

条件付き型がジェネリック型に作用する場合、ユニオン型が指定されると分散型になります。

つまり、条件型がジェネリック型に作用する場合、ユニオン型が分割されて使用されます。つまり、Demo<'a' | 'b' | 'c', 'a'> は、'a' extends 'a'、'b' extends 'a'、'c' extends 'a' に分割されます。疑似コードでは次のようになります:

関数Demo(T, U) {
  T.map(val => { を返す
    if (val !== U) valを返す
    'never' を返す
  })
}

Demo(['a', 'b', 'c'], 'a') // ['never', 'b', 'c']

さらに、never 型の定義によれば、never 型はすべての型に代入可能ですが、never にはどの型も代入できません (never 自身を除く)。つまり、 | 'b' | 'c' は 'b' | 'c' と同等ではありません。

したがって、Demo<'a' | 'b' | 'c', 'a'> の結果は 'a' | 'b' | 'c' ではなく 'b' | 'c' になります。

ツールタイプ

注意深い読者は、Demo 型の宣言プロセスが実際には TypeScript によって公式に提供されるツール型の Exclude<Type, ExcludedUnion> の実装原理であり、Type 型から共用体型 ExcludedUnion を除外するために使用されることに気付いたかもしれません。

type T = Demo<'a' | 'b' | 'c', 'a'> // T: 'b' | 'c'

デモ型定義に基づいて、公式ツール型にOmit<Type, Keys>をさらに実装することができます。これは、オブジェクトTypeを削除するために使用されます。
キー タイプを満たす属性値。

型 Omit<Type, Keys> = {
  [P in Demo<keyof Type, Keys>]: Type<P>
}

インターフェースTodo {
  タイトル: 文字列;
  説明: 文字列;
  完了: ブール値;
}

type T = Omit<Todo, 'description'> // T: { title: string; complete: boolean }

脱出ポッド

Demo<'a' | 'b' | 'c', 'a'> の結果を 'a' | 'b' | 'c' にしたい場合、それは可能ですか? 公式 Web サイトの説明によると:

通常、分散性が望ましい動作です。その動作を回避するには、extends キーワードの両側を角括弧で囲みます。

ジェネリック内のすべての型を反復処理したくない場合は、ジェネリックを角括弧で囲んで、ジェネリックを使用する部分全体を示すことができます。
型 Demo<T, U> = [T] は [U] を拡張しますか? 決して: T

型 Demo<T, U> = [T] は [U] を拡張しますか? 決して: T

// 結果の型は 'a' | 'b' | 'c' です
タイプ結果 = Demo<'a' | 'b' | 'c', 'a'>

矢印関数で条件型を使用する

矢印関数で三項式を使用する場合、括弧で囲まないと、左から右に読む習慣により関数のコンテンツ領域がわかりにくくなります。たとえば、次のコードでは、 x は関数型ですか、それともブール型ですか?

// 意図が明確ではありません。
var x = a => 1 ? 真: 偽

eslint ルール no-confusing-arrow では、以下が推奨されます。

var x = a => (1 ? 真: 偽)

TypeScript の型定義では、矢印関数で extends が使用されている場合も同様です。左から右に読む習慣があるため、型コードの実行順序について読者が混乱する可能性があります。

型 Curry<P extends any[], R> =
  (引数: Head<P>) => HasTail<P> は true を拡張しますか? Curry<Tail<P>, R> : R

したがって、矢印関数で extends を使用する場合は括弧を追加することをお勧めします。これはコードレビューに非常に役立ちます。

型 Curry<P extends any[], R> =
  (引数: Head<P>) => (HasTail<P> は true を拡張しますか? Curry<Tail<P>, R> : R)

型推論による条件型の使用

TypeScript では、型推論構文は通常、extends と組み合わせて使用​​されます。型を自動的に推論するという目的を達成するために使用します。たとえば、関数 Type の戻り値の型を返すために使用されるツール タイプ ReturnType<Type> を実装するために使用できます。
type ReturnType<T extends Function> = T extends (...args: any) => infer U ? U : never

type ReturnType<T extends Function> = T extends (...args: any) => infer U ? U : never

MyReturnType<() => 文字列> // 文字列
MyReturnType<() => Promise<boolean> // Promise<boolean>

extends と型推論を組み合わせると、配列関連の Pop<T>、Shift<T>、および Reverse<T> ツール型も実装できます。

ポップ<T>:

type Pop<T extends any[]> = T extends [...infer ExceptLast, any] ? ExceptLast : never

タイプ T = Pop<[3, 2, 1]> // T: [3, 2]

Shift<T>:

type Shift<T extends any[]> = T extends [infer _, ...infer O] ? O : なし

タイプ T = Shift<[3, 2, 1]> // T: [2, 1]

逆順<T>

型 Reverse<T> = T は [F を推論し、...Others を推論] を拡張します。
  ? [...逆<その他>, F]
  : []

type T = Reverse<['a', 'b']> // T: ['b', 'a']

条件型を使用して、2つの型が完全に等しいかどうかを判断します。

条件型を使用して、型 A と型 B が完全に等しいかどうかを判断することもできます。現在、コミュニティには 2 つの主な解決策があります。

解決策 1: 問題を参照してください。

エクスポート型Equal1<T, S> =
  [T]は[S]を拡張しますか?(
    [S] は [T] を拡張しますか? 真: 偽
  ) : 間違い

現時点でこのソリューションの唯一の欠点は、どのタイプも他のタイプと同等であると判断されることです。

type T = Equal1<{x:any}, {x:number}> // T: true

解決策 2: 問題を参照してください。

エクスポート型Equal2<X, Y> =
  (<T>() => TはXを拡張しますか? 1:2)は拡張します
  (<U>() => U は Y を拡張しますか? 1 : 2) ? 真: 偽

このソリューションの唯一の欠点は、交差タイプの処理にいくつかの欠陥があることです。

type T = Equal2<{x:1} & {y:2}, {x:1, y:2}> // 偽

型が等しいかどうかを判断する上記の 2 つの方法についてはさまざまな意見があり、著者は議論を刺激するためにいくつかのアイデアを提示しているだけです。

要約する

これで、TypeScript の条件型を集中的に読み、実践するこの記事は終了です。TypeScript の条件型に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • TypeScript 列挙の基本と例
  • Typescript+React でモバイルと PC でシンプルなドラッグ アンド ドロップ効果を実現
  • オブジェクトのプロパティを反復処理する際の TypeScript の問題

<<:  IDEA が MySQL データベースに接続できない問題の 6 つの解決策

>>:  LinuxにNginxをインストールする詳細な手順

推薦する

Alibaba Cloud で静的ウェブサイトを素早く構築する方法

序文:ジュニアプログラマーとして、私は自分自身の個人ウェブサイトを構築し、それを他の人に見せることを...

HTML ページ内の js および css ファイルのキャッシュを自動的にクリーンアップします (バージョン番号を自動的に追加します)

Web プロジェクトの開発プロセスでは、CSS ファイルや JS ファイルを参照することがよくあり...

Vueはスクロールバースタイルを実装します

最初はブラウザのスクロールバーのスタイルを変更して効果を実現したいと思っていましたが、情報を調べてみ...

Vueでフォームデータを取得する方法

目次必要データを取得して送信するテンプレートフィルターフィルターの使用シナリオ要約する必要Vue を...

CSSアニメーション効果アニメーションの一般的なスタイル

アニメーションアニメーションを定義します。 /*アニメーションの各ステップで実行されるアクションを定...

Windows 10 で MySQL をダウンロードするための詳細なチュートリアル

MySQL のバージョンは、Enterprise Edition と Community Editi...

Vueタイマーの詳細な使い方

この記事では、参考までにタイマーを実装するためのVueの具体的なコードを紹介します。具体的な内容は次...

ウィンドウ内のさまざまな距離/スクロール距離の正確な計算の概要

通常、プロジェクト開発では、マージン、位置、座標などを扱う必要があります。悲劇なのは、これらの概念が...

Windows 10 に Apache 2.4.41 をインストールするチュートリアル

1. Apache 2.4.41 のインストールと設定最初のステップは、以下に示すように、https...

Mysql 主キー UUID と自動増分主キーの違いと利点と欠点

導入私はしばらくの間、postgresql データベースを使用していました。クラウドに移行した後、自...

圧縮パッケージを使用して Linux 環境に JDK 13 をインストールする方法

JDK とは何ですか?まあ、この質問がわからないのであれば、なぜこれをインストールするのか本当にわか...

MySQL ストアド関数(カスタム関数)の定義と使用方法の詳細な説明

ストアド関数ストアド関数とは: SQL コードの一部をカプセル化し、特定の関数を完了して、結果を返し...

Nginx 構成の実装 HTTPS セキュリティ認証

1. HttpとHttpsの違いHTTP: インターネットで最も広く使用されているネットワーク プロ...

Linuxでスワップパーティションファイルを作成する方法

スワップの紹介Linux のスワップ (スワップ パーティション) は、Windows の仮想メモリ...