Gibt es einen "Wert von" ähnlich dem "Schlüssel" in TypeScript?


118

Ich möchte in der Lage sein, einem Wert, der einen Schlüssel und einen Wert als Eingabe hat, eine Objekteigenschaft zuzuweisen, aber dennoch den Typ des Werts bestimmen können. Es ist etwas schwer zu erklären, daher sollte dieser Code das Problem aufdecken:

type JWT = { id: string, token: string, expire: Date };
const obj: JWT = { id: 'abc123', token: 'tk01', expire: new Date(2018, 2, 14) };

function print(key: keyof JWT) {
    switch (key) {
        case 'id':
        case 'token':
            console.log(obj[key].toUpperCase());
            break;
        case 'expire':
            console.log(obj[key].toISOString());
            break;
    }
}

function onChange(key: keyof JWT, value: any) {
    switch (key) {
        case 'id':
        case 'token':
            obj[key] = value + ' (assigned)';
            break;
        case 'expire':
            obj[key] = value;
            break;
    }
}

print('id');
print('expire');
onChange('id', 'def456');
onChange('expire', new Date(2018, 3, 14));
print('id');
print('expire');

onChange('expire', 1337); // should fail here at compile time
print('expire'); // actually fails here at run time

Ich habe versucht, zu wechseln value: any, value: valueof JWTaber das hat nicht funktioniert.

Idealerweise onChange('expire', 1337)würde dies fehlschlagen, da 1337es sich nicht um einen Datumstyp handelt.

Wie kann ich value: anyden Wert des angegebenen Schlüssels ändern ?

Antworten:


218

UPDATE: Es sieht so aus, als würde der Fragentitel Leute anziehen, die nach einer Vereinigung aller möglichen Eigenschaftswerttypen suchen, analog zu der Art und Weise, wie keyofSie die Vereinigung aller möglichen Eigenschaftsschlüsseltypen erhalten. Helfen wir zuerst diesen Menschen. Sie können eine ValueOfAnalogie zu erstellenkeyof , indem Sie Nachschlagetypen mit keyof Tals Schlüssel verwenden, wie folgt:

type ValueOf<T> = T[keyof T];

was gibt dir

type Foo = { a: string, b: number };
type ValueOfFoo = ValueOf<Foo>; // string | number

Für die angegebene Frage können Sie einzelne Schlüssel verwenden, die schmaler als keyof Tsind, um nur den Werttyp zu extrahieren, den Sie interessieren:

type sameAsString = Foo['a']; // lookup a in Foo
type sameAsNumber = Foo['b']; // lookup b in Foo

Um sicherzustellen, dass das Schlüssel / Wert-Paar in einer Funktion richtig "übereinstimmt", sollten Sie sowohl Generika als auch Nachschlagetypen wie folgt verwenden:

declare function onChange<K extends keyof JWT>(key: K, value: JWT[K]): void; 
onChange('id', 'def456'); // okay
onChange('expire', new Date(2018, 3, 14)); // okay
onChange('expire', 1337); // error. 1337 not assignable to Date

Die Idee ist, dass der keyParameter es dem Compiler ermöglicht, auf den generischen KParameter zu schließen . Dann erfordert es, dass valueÜbereinstimmungen JWT[K], der von Ihnen benötigte Nachschlagetyp.

Hoffentlich hilft das; Viel Glück!


Ich möchte ein tupleaus Werten erstellen . Gibt es eine Chance dazu?
— Morteza Tourani

Das klingt nach einer nicht verwandten Frage, daher sollten Sie sie wahrscheinlich woanders stellen, mit einem minimal reproduzierbaren Beispiel, damit die Leute Ihren Anwendungsfall sicher verstehen. Viel Glück.
— Jcalz

1
Bei der Verwendung einer Zeichenfolgenaufzählung mit Funktionselementen ist ein Problem aufgetreten. Um dies gut zu handhaben, können Sie verwenden type StringValueOf<T> = T[keyof T] & string;. Die besten Dokumente, die ich in String-Enums gefunden habe, sind die Versionshinweise zu TypeScript 2.9
— karmakaze

41

Wenn jemand noch nach einer Implementierung valueoffür irgendeinen Zweck sucht , ist dies eine, die ich mir ausgedacht habe:

type valueof<T> = T[keyof T]

Verwendung:

type actions = {
  a: {
    type: 'Reset'
    data: number
  }
  b: {
    type: 'Apply'
    data: string
  }
}
type actionValues = valueof<actions>

Funktioniert wie erwartet :) Gibt eine Union aller möglichen Typen zurück


2
Das ist nett. Es löst fast mein anderes Problem , jedoch scheint der Schalter den Typ überhaupt nicht einzugrenzen.
— Styfle

Ich möchte ein tupleaus Werten erstellen . Gibt es eine Chance dazu?
— Morteza Tourani

12

Es gibt eine andere Möglichkeit, den Vereinigungstyp des Objekts zu extrahieren:

  const myObj = { a: 1, b: 'some_string' } as const;
  type values = typeof myObj[keyof typeof myObj];

Ergebnis: 1 | "some_string"


Diese constSache ist sehr wertvoll. TypeScript stellt die Werte tatsächlich selbst bereit und entfernt Duplikate. Es ist sehr gut für Wörterbücher.
— Noam

Genau das, wonach ich gesucht habe!
— Mavrick RMX '21.

2

Vielen Dank für die vorhandenen Antworten, die das Problem perfekt lösen. Ich wollte nur eine Bibliothek hinzufügen, die diesen Dienstprogrammtyp enthält, wenn Sie diesen allgemeinen importieren möchten.

https://github.com/piotrwitek/utility-types#valuestypet

import { ValuesType } from 'utility-types';

type Props = { name: string; age: number; visible: boolean };
// Expect: string | number | boolean
type PropsValues = ValuesType<Props>;

2

Versuche dies:

type ValueOf<T> = T extends any[] ? T[number] : T[keyof T]

Es funktioniert auf einem Array oder einem einfachen Objekt.

// type TEST1 = boolean | 42 | "heyhey"
type TEST1 = ValueOf<{ foo: 42, sort: 'heyhey', bool: boolean }>
// type TEST2 = 1 | 4 | 9 | "zzz..."
type TEST2 = ValueOf<[1, 4, 9, 'zzz...']>

0

Einzeiler:

type ValueTypesOfPropFromMyCoolType = MyCoolType[keyof MyCoolType];

Beispiel für eine generische Methode:

declare function doStuff<V extends MyCoolType[keyof MyCoolType]>(propertyName: keyof MyCoolType, value: V) => void;
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.