Was ist der Datensatztyp im Typoskript?


180

Was Record<K, T>bedeutet in Typescript?

Typescript 2.1 führte den RecordTyp ein und beschrieb ihn in einem Beispiel:

// 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>

siehe Typoskript 2.1

Und die erweiterte Typen Seite erwähnt Recordunter dem Mapped Typ Überschrift neben Readonly, Partialund Pickin dem, was scheint seine Definition zu sein:

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

Readonly, Partial und Pick sind homomorph, Record hingegen nicht. Ein Hinweis darauf, dass Record nicht homomorph ist, ist, dass kein Eingabetyp zum Kopieren von Eigenschaften erforderlich ist von:

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

Und das ist es. Außer den obigen Zitaten gibt es keine andere Erwähnung Recordauf typescriptlang.org .

Fragen

  1. Kann jemand eine einfache Definition dessen geben, was Recordist?

  2. Ist es Record<K,T>nur eine Art zu sagen, dass "alle Eigenschaften dieses Objekts einen Typ haben T"? Wahrscheinlich nicht alle Eigenschaften, da Khat einen Zweck ...

  3. Verbietet das KGenerikum zusätzliche Schlüssel für das Objekt, die dies nicht sind?K , oder erlaubt es sie und zeigt nur an, dass ihre Eigenschaften nicht transformiert werden T?

  4. Mit dem gegebenen Beispiel:

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

Ist es genau das gleiche?:

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

6
Die Antwort auf 4. ist so ziemlich "Ja", also sollte das wahrscheinlich Ihre anderen Fragen beantworten.
Jcalz

Antworten:


208
  1. Kann jemand eine einfache Definition dessen geben, was Recordist?

A Record<K, T>ist ein Objekttyp, dessen Eigenschaftsschlüssel Kund dessen Eigenschaftswerte sind T. Das heißt, keyof Record<K, T>ist äquivalent zu Kund Record<K, T>[K]ist (im Grunde) äquivalent zu T.

  1. Ist es Record<K,T>nur eine Art zu sagen, dass "alle Eigenschaften dieses Objekts einen Typ haben T"? Wahrscheinlich nicht alle Objekte, da Khat einen Zweck ...

Wie Sie bemerken, Khat es einen Zweck ... die Eigenschaftsschlüssel auf bestimmte Werte zu beschränken. Wenn Sie alle möglichen Schlüssel mit Zeichenfolgenwerten akzeptieren möchten, können Sie Folgendes tun. Record<string, T>Die idiomatische Methode hierfür ist die Verwendung einer Indexsignatur wie { [k: string]: T }.

  1. Verbietet das KGenerikum zusätzliche Schlüssel für das Objekt, die es nicht sindK , oder erlaubt es sie und zeigt nur an, dass ihre Eigenschaften nicht transformiert werden T?

Es "verbietet" zusätzliche Schlüssel nicht genau: Schließlich darf ein Wert im Allgemeinen Eigenschaften haben, die in seinem Typ nicht explizit erwähnt werden ... aber es würde nicht erkennen, dass solche Eigenschaften existieren:

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

und es würde sie als überschüssige Eigenschaften behandeln, die manchmal abgelehnt werden:

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

und manchmal akzeptiert:

const y = {a: "hey", b: "you"};
acceptR(y); // okay
  1. Mit dem gegebenen Beispiel:

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

    Ist es genau das gleiche?:

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

Ja!

Hoffentlich hilft das. Viel Glück!


1
Sehr viel gelernt und eine Frage, warum "die idiomatische Art, dies zu tun, darin besteht, eine Indexsignatur zu verwenden", keine Datensatzsignatur? Ich finde keine verwandten Informationen über diesen "idiomatischen Weg".
legend80s

2
Sie können verwenden, um Record<string, V>zu bedeuten, {[x: string]: V}wenn Sie wollen; Ich habe das wahrscheinlich sogar selbst gemacht. Die Indexsignaturversion ist direkter: Sie sind vom gleichen Typ, aber der erstere ist ein Typalias eines zugeordneten Typs, der als Indexsignatur ausgewertet wird, während der letztere nur die Indexsignatur direkt ist. Wenn alles andere gleich ist, würde ich letzteres empfehlen. Ebenso würde ich nicht Record<"a", string>anstelle von verwenden, {a: string}wenn es nicht einen anderen zwingenden kontextuellen Grund dafür gäbe.
Jcalz

1
Wenn alles andere gleich ist, würde ich letzteres empfehlen. “ Warum ist das so? Mein Pre-Typescript-Selbst stimmt zu, aber ich weiß, dass das erstere mehr, ähm, selbstkommentierend für Leute sein wird, die zum Beispiel von der C # -Seite kommen, und nicht schlechter für JavaScript-to-Typescripters. Möchten Sie nur den Transpilationsschritt für diese Konstrukte überspringen?
Ruffin

1
Nur meine Meinung: Das Verhalten von Record<string, V>macht nur Sinn, wenn Sie bereits wissen, wie Indexsignaturen in TypeScript funktionieren. Beispiel gegeben x: Record<string, string>, x.foowird offenbar sein , stringbei der Kompilierung, aber in Wirklichkeit ist wahrscheinlich zu sein string | undefined. Dies ist eine Lücke in der Funktionsweise --strictNullChecks(siehe # 13778 ). Ich würde eher Neulinge mit beschäftigen {[x: string]: V}direkt , anstatt sie zu erwarten , dass die Kette aus folgt Record<string, V>bis {[P in string]: V}zum Index Signaturverhalten.
Jcalz

Ich wollte darauf hinweisen, dass ein Typ logischerweise als eine Menge aller im Typ enthaltenen Werte definiert werden kann. Angesichts dieser Interpretation halte ich Record <string, V> für eine Abstraktion, um den Code zu vereinfachen, anstatt alle möglichen Werte anzugeben. Es ähnelt dem Beispiel in der Dokumentation zu Dienstprogrammtypen: Zeichnen Sie <T, V> auf, wobei Typ T = 'a' | ist 'b' | 'c'. Niemals Record <'a', string> ausführen ist kein gutes Gegenbeispiel, da es nicht dem gleichen Muster folgt. Es wird auch nicht hinzugefügt, um den Code durch Abstraktion wiederzuverwenden oder zu vereinfachen, wie dies bei den anderen Beispielen der Fall ist.
Scott Leonard

68

Mit einem Datensatz können Sie einen neuen Typ aus einer Union erstellen. Die Werte in der Union werden als Attribute des neuen Typs verwendet.

Angenommen, ich habe eine Union wie diese:

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

Jetzt möchte ich ein Objekt erstellen, das Informationen zu allen Katzen enthält. Ich kann einen neuen Typ erstellen, indem ich die Werte in der CatName Union als Schlüssel verwende.

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

Wenn ich diese CatList erfüllen möchte, muss ich ein Objekt wie das folgende erstellen:

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

Sie erhalten eine sehr starke Typensicherheit:

  • Wenn ich eine Katze vergesse, erhalte ich eine Fehlermeldung.
  • Wenn ich eine Katze hinzufüge, die nicht erlaubt ist, wird eine Fehlermeldung angezeigt.
  • Wenn ich später CatNames ändere, wird eine Fehlermeldung angezeigt. Dies ist besonders nützlich, da CatNames wahrscheinlich aus einer anderen Datei importiert und wahrscheinlich an vielen Stellen verwendet wird.

Beispiel für eine reale Reaktion.

Ich habe dies kürzlich verwendet, um eine Statuskomponente zu erstellen. Die Komponente würde eine Status-Requisite erhalten und dann ein Symbol rendern. Ich habe den Code hier zur Veranschaulichung ziemlich stark vereinfacht

Ich hatte eine Gewerkschaft wie diese:

type Statuses = "failed" | "complete";

Ich habe dies verwendet, um ein Objekt wie das folgende zu erstellen:

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

Ich könnte dann rendern, indem ich ein Element aus dem Objekt in Requisiten zerlege, wie folgt:

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

Wenn die Statusvereinigung später erweitert oder geändert wird, kann meine Statuskomponente nicht kompiliert werden, und es wird ein Fehler angezeigt, den ich sofort beheben kann. Dadurch kann ich der App zusätzliche Fehlerzustände hinzufügen.

Beachten Sie, dass die eigentliche App Dutzende von Fehlerzuständen aufwies, auf die an mehreren Stellen verwiesen wurde. Daher war diese Art der Sicherheit äußerst nützlich.


Ich type Statusesgehe davon aus, dass die meiste Zeit in von Ihnen NICHT definierten Schreibweisen lebt. Ansonsten kann ich so etwas wie eine Schnittstelle mit einer Aufzählung sehen, die besser passt, oder?
Victorio Berra

Hallo @victorio, ich bin nicht sicher, wie eine Aufzählung das Problem lösen würde. Sie erhalten keinen Fehler in einer Aufzählung, wenn Sie einen Schlüssel verpassen. Es ist nur eine Zuordnung zwischen Schlüsseln und Werten.
Superluminary

1
Ich verstehe, was du jetzt meinst. Von C # kommend haben wir keine klugen Möglichkeiten, das zu tun. Das nächste wäre ein Wörterbuch von Dictionary<enum, additional_metadata>. Der Datensatztyp ist eine hervorragende Möglichkeit, dieses Enum + Metadatenmuster darzustellen.
Victorio Berra
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.