typescriptのRecordタイプとは何ですか?


183

Record<K, T>Typescript とはどういう意味ですか?

Typescript 2.1はRecordタイプを導入し、例でそれを説明しました:

// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>

Typescript 2.1を参照

また、[ Advanced Types]ページではRecord、マップされたタイプの見出しの下に、その定義のように見えるもので、、、およびの横Readonlyに言及しています。PartialPick

type Record<K extends string, T> = {
    [P in K]: T;
}

Readonly、Partial、Pickは準同型ですが、Recordは同型ではありません。Recordが準同型ではないことの1つの手掛かりは、プロパティをコピーするために入力タイプをとらないことです。

type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

以上です。上記の引用の他にRecordtypescriptlang.orgでの言及は他にありません。

ご質問

  1. 誰かが何かを簡単に定義できますかRecord

  2. Record<K,T>、単に「このオブジェクトのすべてのプロパティが型を持っているだろうと言っての道T」?いくつかの目的があるので、おそらくすべてのプロパティではKありません...

  3. Kジェネリックは、オブジェクトに追加されていない追加のキーを禁止しますかK、それとも許可し、プロパティが変換されていないことを示すだけTですか?

  4. 与えられた例で:

     type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

これとまったく同じですか?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}

6
4.への答えはほぼ「はい」なので、おそらく他の質問に答えるはずです。
jcalz

回答:


211
  1. 誰かが何かを簡単に定義できますかRecord

A Record<K, T>は、プロパティキーがKあり、プロパティ値がであるオブジェクトタイプですT。つまり、keyof Record<K, T>と同等でありKRecord<K, T>[K](基本的に)と同等Tです。

  1. Record<K,T>、単に「このオブジェクトのすべてのプロパティが型を持っているだろうと言っての道T」?K何らかの目的があるので、おそらくすべてのオブジェクトではありません...

お気づきのように K目的を持っている...特定の値にプロパティキーを制限します。可能なすべての文字列値のキーを受け入れたい場合は、のようなことを行うことができますがRecord<string, T>、これを行う慣用的な方法は、のようなインデックス署名を使用することです{ [k: string]: T }

  1. Kジェネリックは、オブジェクトに追加されていない追加のキーを禁止しますかK、それとも許可し、プロパティが変換されていないことを示すだけTですか?

追加のキーを厳密に「禁止」するわけではありません。結局のところ、値は通常、その型で明示的に言及されていないプロパティを持つことが許可されています...しかし、そのようなプロパティが存在することを認識しません:

declare const x: Record<"a", string>;
x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'

そしてそれはそれらを は時々拒否さ過剰なプロパティ

declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties

そして時々受け入れられました:

const y = {a: "hey", b: "you"};
acceptR(y); // okay
  1. 与えられた例で:

    type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

    これとまったく同じですか?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}

はい!

お役に立てば幸いです。幸運を!


1
非常に多くのことを学び、なぜ「それを行う慣用的な方法は、インデックスシグネチャを使用することである」のか、それがRecordのシグネチャではないのかという疑問です。この「慣用的な方法」に関する関連情報は見つかりません。
legend80s

2
あなたは使用することができますRecord<string, V>を意味するように{[x: string]: V}したい場合は、私もおそらくこれを自分でやったこともあります。インデックスシグネチャのバージョンはより直接的です。これらは同じ型ですが、前者はマップされた型の型エイリアスであり、インデックスシグネチャとして評価されますが、後者は直接インデックスシグネチャです。他はすべて同じなので、後者をお勧めします。同様に、他の説得力のある文脈上の理由がない限りRecord<"a", string>、代わりにを使用しません{a: string}
jcalz

1
他はすべて同じなので、後者をお勧めします。」なぜですか?私のプレタイプスクリプトは自己同意しますが、前者はたとえばC#側から来た人々にとっては自己コメントが多く、JavaScriptからタイプスクリプトへの悪影響はないことを知っています。これらの構成要素のトランスパイレーション手順をスキップすることに興味がありますか?
ruffin

1
Record<string, V>TypeScriptでインデックスシグネチャがどのように機能するかをすでに知っている場合にのみ、私の意見です。たとえば、与えられたようにx: Record<string, string>x.foo明らかstringにコンパイル時になりますが、実際にはそうなりますstring | undefined。これは動作のギャップです--strictNullChecks#13778を参照)。私はむしろ新規参入が扱う必要があるだろう{[x: string]: V}から、チェーンをたどるためにそれらを期待しての代わりに、直接Record<string, V>通じ{[P in string]: V}インデックス署名行動に。
jcalz

論理的には、型はその型に含まれるすべての値のセットとして定義できることを指摘したかったのです。この解釈を考えると、可能なすべての値をレイアウトするのではなく、コードを簡略化する抽象化としてRecord <string、V>は合理的だと思います。これは、ユーティリティタイプのドキュメントの例に似ています。Record <T、V> where type T = 'a' | 'b' | 「c」。Record <'a'、string>を実行しないことは、同じパターンに従っていないため、良い反例ではありません。また、他の例のように、抽象化によってコードを再利用または簡略化することはできません。
スコットレナード

69

Recordを使用すると、Unionから新しいタイプを作成できます。Unionの値は、新しいタイプの属性として使用されます。

たとえば、次のようなUnionがあるとします。

type CatNames = "miffy" | "boris" | "mordred";

すべての猫に関する情報を含むオブジェクトを作成したいので、CatName Unionの値をキーとして使用して新しいタイプを作成できます。

type CatList = Record<CatNames, {age: number}>

このCatListを満たしたい場合は、次のようなオブジェクトを作成する必要があります。

const cats:CatList = {
  miffy: { age:99 },
  boris: { age:16 },
  mordred: { age:600 }
}

非常に強力な型安全性が得られます。

  • 猫を忘れるとエラーになります。
  • 許可されていない猫を追加すると、エラーが発生します。
  • 後でCatNamesを変更すると、エラーが発生します。CatNamesは別のファイルからインポートされ、多くの場所で使用される可能性が高いため、これは特に便利です。

実際のReactの例。

最近これを使用してStatusコンポーネントを作成しました。コンポーネントはステータスプロップを受け取り、アイコンをレンダリングします。ここでは、説明のためにコードを大幅に簡略化しました

私はこのような労働組合を持っていました:

type Statuses = "failed" | "complete";

これを使用して、次のようなオブジェクトを作成しました。

const icons: Record<
  Statuses,
  { iconType: IconTypes; iconColor: IconColors }
> = {
  failed: {
    iconType: "warning",
    iconColor: "red"
  },
  complete: {
    iconType: "check",
    iconColor: "green"
  };

次に、次のように、要素をオブジェクトからプロップに分解することでレンダリングできます。

const Status = ({status}) => <Icon {...icons[status]} />

Statusesユニオンが後で拡張または変更された場合、Statusコンポーネントがコンパイルに失敗し、すぐに修正できるエラーが表示されます。これにより、アプリにエラー状態を追加できます。

実際のアプリには複数の場所で参照される数十のエラー状態があったため、この型の安全性は非常に役立ちました。


ほとんどのtype Statuses場合、あなたが定義していないタイピングに住んでいると思いますか?それ以外の場合、列挙型が適切なインターフェイスのようなものが表示されますか?
Victorio Berra

こんにちは@victorio、列挙型がどのように問題を解決するかわかりません。キーを見逃しても、列挙型でエラーが発生することはありません。これは、キーと値の間の単なるマッピングです。

1
私はあなたが今何を意味するかを理解しています。C#から来るので、そのための賢い方法はありません。最も近いものはの辞書ですDictionary<enum, additional_metadata>。Recordタイプは、その列挙型+メタデータパターンを表す優れた方法です。
Victorio Berra
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.