再帰的な条件付きタイプ

Barbara Streisand
Barbara Streisandオリジナル
2024-12-01 03:00:13462ブラウズ

Recursive Conditional Types

for in ループがあるとします。変数の型が文字列であり、文字列リテラル共用体型ではないことに突然気づきました。そのため、アプリを tsc でコンパイルしているときにこの醜いエラーが発生し、迷惑なことに、お気に入りの IDE が全力で叫び声を上げる可能性が高くなります。

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ user: number; nice: number; sys: number; idle: number; irq: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ user: number; nice: number; sys: number; idle: number; irq: number; }'.ts(7053)

[!NOTE]

それがどのように行われるかを示すために、私は os.cpus を使用しています。そこで、オブジェクトである cpu.times をループしようとします。詳細については、こちらをご覧ください。

問題のあるコードは次のとおりです。

import { cpus } from 'os';

const logicalCoresInfo = cpus();

for (const logicalCoreInfo of logicalCoresInfo) {
  let total = 0;
  for (const type in logicalCoreInfo.times) {
    total += logicalCoreInfo.times[type];  // Darn it, TS is upset!
  }
}

修理

  1. logicalCoreInfo.times 内のキーを抽出し、そこから新しいタイプを作成する必要があります。
  2. その後、型アサーションを利用して、TS にすべてが素晴らしく、ここで何が起こっているかを理解していることを納得させることができます。

それでは本題に入りましょう。最初の部分では、独自にカスタム ユーティリティ タイプを作成する必要があります。最終的なユーティリティ タイプは次のようになります。

type NestedKeysOf<T, K extends PropertyKey> = T extends object
  ? {
      [TKey in keyof T]-?:
        | (TKey extends K ? keyof T[TKey] : never)
        | NestedKeysOf<T[TKey], K>;
    }[keyof T]
  : never;

詳しく見てみましょう:

  1. T はオブジェクトを拡張しますか? ... : Never は、T がオブジェクトの場合、ネストされたオブジェクト型 T を再帰的に走査し、T 内の特定のキーのキーを抽出するように TS に指示しています。
  2. [TKey in keyof T]-?これは、このユーティリティ タイプに渡されるオブジェクト内のキーの名前がわからないため、ここでは特に便利な「マップされたタイプ」です。ここでは、logicalCoreInfo をオブジェクトまたは他のオブジェクトに渡し、キーを反復処理してキーから新しい型を作成します。

    そして -?オプション性を削除して、すべてのキーの文字列リテラル共用体型を取得するためにあります。つまり、 { keyName?: string } は { keyName: string } として扱われます。

  3. (TKey extends K ? keyof T[TKey] : Never) 反復内の現在のキーが渡されたキー (K) と一致するかどうかを確認し、一致する場合は、その中のすべてのキーを文字列リテラル共用体型として抽出します。そしてそれを返します。それ以外の場合は何も返しません。

  4. ステップ 3 で結果が得られなかった場合は、このユーティリティ タイプを T[Tkey] に再帰的に適用します。これにより、ユーティリティ関数はネストされたオブジェクトに対しても動作します。これは一般に「再帰的条件型」として知られています。

  5. 最後に、マップされた型によって生成されたすべての型の結合を取得するように要求しています。つまり、入れ子構造を平坦化しています。

それでは、今度はそれを使用します:

interface Person {
  name: string;
  address: {
    street: string;
    city: string;
  };
}

type KeysOfAddress = NestedKeysOf<Person, 'address'>; // "street" | "city"

// Or in our original example:
type CpuTimesKeys = NestedKeysOf<typeof logicalCoreInfo, 'times'>;
// ...
total += logicalCoreInfo.times[type as CpuTimesKeys];
// ...

参照

  • ネストされたオブジェクト型の TypeScript keyof。

以上が再帰的な条件付きタイプの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。