typescript - Konvertera en lista med nyckel-värde-par-gränssnitt till en enda mappad typ


Jag har ett set av gränssnitt som så:

interface Foo {
    key: "fooKeyType",
    value: "fooValueType",
}

interface Bar {
    key: "barKeyType",
    value: "barValueType",
}

interface Baz {
    key: "bazKeyType",
    value: "bazValueType",
}

type FooBarBaz = Foo | Bar | Baz;
type FooBarBazKey = FooBarBaz["key"];
type FooBarBazValue = FooBarBaz["value"];

där *keyType alltid är konstanta strängar (så att de är giltiga nycklar), men *valueType är bara platshållare för vad som kan vara faktiska typer (t.ex. number, Date, egna klasser osv).

Jag vill konvertera dessa gränssnitt till en enskild typkarta som mappar nyckel -> värde, såsom följande:

/* Can I generate this from the above? */
type FooBarBazMap = {
    fooKeyType: "fooValueType",
    barKeyType: "barValueType",
    bazKeyType: "bazValueType",
}

Finns det ett sätt att göra detta med mappade typer? Mappade typer verkar inte låta dig referera till den för närvarande indexerade typen, bara typen som helhet, vilket betyder att du inte kan koppla ihop fält från samma gränssnitt.

Svar #1

Ja. Allt kokar ner till att omvandla denna typ:

type KeyValue<K extends string, V> = {
    key: K,
    value: V,
};

...till detta:

type Desired<K extends string, V> = {
    [key in K]: V;
};

Idealiskt skulle vi vilja skriva något som detta:

type TransformKeyValueWrong<T extends KeyValue<T["key"], T["value"]>> = {
     [key in T["key"]]: T["value"];
};

Men TypeScript-kompilatorn skulle skrika åt oss "[ts] Typparametern 'key' har ett cirkulärt begränsning.", vilket är sant, men det kommer inte stoppa oss, eftersom vi har generiska parameterstandarder!

type TransformKeyValue<T extends KeyValue<K, T["value"]>, K extends string = T["key"]> = {
    [key in K]: T["value"];
};

Allt vi behöver göra nu är att deklarera en intersection-typ för Foo, Bar, Baz:

type SuperDuperType =
    & TransformKeyValue<Foo>
    & TransformKeyValue<Bar>
    & TransformKeyValue<Baz>
    ;

Verifera att det fungerar:

type Test = SuperDuperType["barKeyType"]; // type Test = "barValueType";

Observera att standardvärden för generiska parametrar kräver TypeScript-version >=2.3