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をインストールする詳細な手順

推薦する

ウェブページのテーブルの境界線を設定する方法

<br />前回は、Web テーブルにセルの線を設定する方法を学びました。今日は、Web...

MySQL 8.0の新機能、隠しフィールドの詳細な説明

序文MySQL バージョン 8.0.23 では、新しい機能「Invisible Column (In...

MySQLデータベースは重複データを削除し、メソッドインスタンスを1つだけ保持します

1. 問題の紹介ユーザー テーブルに 3 つのフィールドが含まれているシナリオを想定します。 id、...

ポップアップ効果を実現するにはjsを使用します

この記事の例では、ポップアップ効果を実現するためのjsの具体的なコードを参考までに共有しています。具...

Linux で Grafana をインストールし、InfluxDB モニタリングを追加する方法

Grafana をインストールします。公式 Web サイトでは、直接インストールできる Ubuntu...

MySQL の時間タイプとモードの詳細

目次1. MySQL の時刻型2. タイムゾーンを確認する3. 不正な時間値4. 厳密モード5. 事...

Vueパンくずコンポーネントのカプセル化方法

Vueはパンくずコンポーネントをカプセル化して参照します。具体的な内容は次のとおりです。効果を達成す...

jQuery は拡張アニメーションによるナビゲーション バー効果を実装します

展開アニメーション効果のあるナビゲーションバーを設計してカスタマイズし、デモを作成してみました。設計...

画像を使用してハイパーリンクのパーソナライズされた下線を実現します

画像内に下線付きのリンクが表示されても驚かないでください。実はとても簡単なので、あなたにもできるので...

ネイティブ js カプセル化シームレスカルーセル機能

ネイティブjsカプセル化シームレスカルーセルプラグイン、参考までに、具体的な内容は次のとおりです。例...

CSS3の3D効果を使って立方体を作成する

CSS3 の 3D 効果を使用して立方体を作成する方法を学ぶと、3D シーンの回転と変位のプロパティ...

HTMLにビデオを挿入してすべてのブラウザと互換性を持たせる方法

HTML にビデオを挿入するために最もよく使用される方法は 2 つあります。1 つは古い <o...

mysql 一時テーブルの使用状況の分析 [クエリ結果は一時テーブルに保存できます]

この記事では、例を使用して MySQL 一時テーブルの使用方法を説明します。ご参考までに、詳細は以下...

DockerコンテナにPythonアプリケーションをデプロイするプロセスの分析

シンプルなアプリケーションの展開1. ディレクトリ構造: └── Pythonpro #ディレクトリ...

ウェブページ上の小さなスペースに大きな画像を配置する方法

出典: www.bamagazine.comウェブページのバナー、ニュースの見出しの周りのスペース、...