typescript - odvodiť typy pre kľúč ku viacerým mapovacím objektom/typom
Zaujímalo by ma, ako vytvoriť mapovania generických typov.
Ako môžete vidieť na obrázku a/alebo na ihrisku..
Kód „beží“ dobre, ale typy sú nejako divné/zmiešané.
Predstavte si, že existuje ešte ďalšie mapovanie na Predmet, takže by som chcel byť schopný urobiť niečo takéto, samozrejme typovo bezpečne, .. :
subjMap[key].next(accessorMap[key]());
Je to vôbec možné s rôznymi objektmi/mapami.. Alebo potrebujem vytvoriť jeden objekt na každé "kľúč", tak povediac..?
type KeyToTypeMapping = {
iaModel: string;
debug: boolean;
};
type MappingKeys = keyof KeyToTypeMapping;
type KeyToTypedAccessor<T> = Record<keyof T, () => T[keyof T]>;
const accessorMap: KeyToTypedAccessor<KeyToTypeMapping> = {
iaModel: () => "string",
debug: () => true
};
const stringResult = accessorMap.iaModel();
// TS tells me that the type of `stringResult` is string | boolean
console.log(stringResult);
type KeyToTypedFn<T> = {
[Key in keyof T]: { next: (_: T[Key]) => void };
};
// here is another map with the same keys and "base-types"..
// .. there could be even more ..
const subjMap: KeyToTypedFn<KeyToTypeMapping> = {
iaModel: { next: (_) => { console.log('next', _); } },
debug: { next: (_) => { console.log('next', _); } },
}
Object.keys(accessorMap).forEach(k => {
// sadly we need to cast here..
const typedKey = k as keyof KeyToTypeMapping;
const val = accessorMap[typedKey]();
// here TS complains that val (type of string | boolean) is not assignable to never ..
subjMap[typedKey].next(val);
});
Odpoveď č. 1
Len nepoužívajte Record, aby ste mohli priamo pristupovať k kľúču!
type KeyToTypeMapping = {
iaModel: string;
debug: boolean;
};
type KeyToTypedAccessor<T> = { [P in keyof T]: () => T[P] }
declare const accessorMap: KeyToTypedAccessor<KeyToTypeMapping>
const stringResult = accessorMap.iaModel();
Detské ihrisko
Odpoveď č. 2
Myslím, že hľadáte namapovaný typ, ktorý by vyzeral takto:
type KeyToTypedAccessor<T> = {
[Key in keyof T]: () => T[Key];
};
Odkaz na ihrisko
Úplný príklad:
type KeyToTypeMapping = {
iaModel: string;
debug: boolean;
};
type KeyToTypedAccessor<T> = {
[Key in keyof T]: () => T[Key];
};
const accessorMap: KeyToTypedAccessor<KeyToTypeMapping> = {
iaModel: () => "string",
debug: () => true
};
const stringResult = accessorMap.iaModel();
// ^? −−−− string
const booleanResult = accessorMap.debug();
// ^? −−−− boolean
console.log(stringResult);
V komentári ste povedali, že ste uviazli na tomto mieste:
Object.keys(accessorMap).forEach(k => {
// sadly we need to cast here..
const typedKey = k as keyof KeyToTypeMapping;
const val = accessorMap[typedKey]();
// here TS complains that val (type of string | boolean) is not assignable to never ..
subjMap[typedKey].next(val);
});
Bohužiaľ, to nemôžete opraviť typovo bezpečným spôsobom bez toho, aby ste niečo nespravili tak, aby TypeScript vedel buď:
Ktorý kľúč z
KeyToTypeMappingjetypedKey, aby vedel, aký typ hodnoty je. Môže to byť buď (iaModelvedúci k hodnote typu string, alebodebugvedúci k hodnote typu boolean).alebo
Že rovnaký kľúč
KeyToTypeMappingsa používa tak pre indexovanie dosubjMap, ako aj doaccessorMap.
#1 vedie k dosť škaredému opakujúcemu sa kódu, takže to nechceme (v tomto prípade).
Čo sa týka #2, mohli by ste si myslieť, že TypeScript by z vášho kódu (alebo ešte zjavnejšie z subjMap[typedKey].next(accessorMap[typedKey]();) vedel, že kľúč je rovnaký, ale chce generickú funkciu, aby mu pomohla:
function setNext<Key extends keyof KeyToTypeMapping>(
subjMap: KeyToTypedFn<KeyToTypeMapping>,
accessorMap: KeyToTypedAccessor<KeyToTypeMapping>,
key: Key,
) {
subjMap[key].next(accessorMap[key]());
}
Potom môžeme urobiť:
Object.keys(accessorMap).forEach((key) => {
// (As you say, you're stuck with the type assertion on `key`.)
setNext(subjMap, accessorMap, key as any as keyof KeyToTypeMapping);
});
Odkaz na ihrisko
(Poznámka: Typové tvrdenia nie sú konverzie. Konverzia [v jazykoch, ktoré ich majú] môže skutočne previesť hodnotu z jednej formy na inú, napríklad prevod dvojkového doplnku int bitového vzoru na IEEE-754 double-precision plávajúci bod double bitový vzor s ekvivalentnou alebo takmer ekvivalentnou číselnou hodnotou, aj keď bitové vzory pre [napríklad] 42 sú medzi týmito dvoma formátmi veľmi odlišné. Typové tvrdenia v TypeScript nikdy nevykonávajú žiadnu konverziu. Dokonca ani neexistujú pri behu programu. Takže sú to len tvrdenia, jednoducho hovoríte TypeScriptu „dôveruj mi, toto ? je X.“ :-) )
