typescript - Hvordan slå sammen alle verdier av et grensesnitt
Jeg prøver å slå sammen alle verdier av et grensesnitt, for eksempel:
interface IA {
a: string;
}
interface IB {
b: string;
}
interface IC {
c: string;
}
//...
interface IMap {
"a": IA;
"b": IB;
"c": IC;
//...
"z": IZ;
}
Finnes det en måte å lage en type på som:
type IAll = IMap["a"] & IMap["b"] & IMap["c"] & ... & IMap["z"];
Jeg prøver å bruke dette begge:
type valueMerge<T> = T[keyof T];
type valueMerge2<T extends keyof IMap = keyof IMap> = IMap[T];
// using
let t: valueof<IMap> = null as any;
let t2: valueof2 = null as any;
Men for hver enkelt er typen IA | IB | IC | ... | IZ og ikke IA & IB & IC & ... & IZ.
Svar #1
Dette ligner på et spørsmål om hvordan man programmessig kan gjøre en union om til et snitt, og det har en lignende løsning.
Normalt, når du gjør et oppslag av alle egenskapene til en objekttype, får du en union av alle egenskapene, som du så. Problemet er at keyof T er en union av egenskapsnøklene, og å gjøre oppslag på en union av nøkler gir deg en union av egenskaper, fordi egenskapsoppslag er kovariant i typen til egenskapsnøkkelen... men det du ønsker er en operasjon kontravariant i typen til egenskapsnøkkelen. (Du kan lese mer om kovarians/kontravarians her, men i utgangspunktet innebærer kovarians at en union som input blir en union som output, mens kontravarians innebærer at en union som input blir et snitt som output).
Heldigvis er funksjonsargumenter kontravariate, så hvis vi kan mappe ditt grensesnitt til et nytt hvor de gamle egenskapstypene er funksjonsargumenter, kan vi komme til et punkt hvor en union av disse funksjonstypene kan resultere i skjæringspunktet av argumentene deres. Og typeinformasjonsinnhenting i betingede typer ved bruk av infer-nøkkelordet kan gjøre dette for oss (se linjen som sier «hvis noen kandidater ble antatt fra kontra-variante posisjoner, er typen som ble antatt et skjæringspunkt av disse kandidatene»).
Her er koden:
type IntersectProperties<T> = { [K in keyof T]: (x: T[K]) => void } extends {
[K in keyof T]: (k: infer I) => void
}
? I
: never;
Egenskapene til T blir kartlagt til funksjonsargumenter, og deretter slutter vi oss til en enkelt type I som tilsvarer alle funksjonsargumentene, som vil være en snitt-type.
La oss se om det fungerer:
type IAll = IntersectProperties<IMap>;
// type IAll = IA & IB & IC & ... & IZ
Ser bra ut. Håper det hjelper; lykke til!
Lenke til kode
