typescript - Tipul 'K' nu poate fi folosit pentru a indexa tipul '{ [key in keyof K]: V; }'.ts(2536)
Vreau să creez o metodă care returnează un obiect nou din tipul meu personalizat de obiect.
/*
type K represent the type of key in an object
type V represent the type of value in an object
*/
class Group<K,V> extends Map<K,V> {
toObject() {
const result: { [key in keyof K]: V } = {} as { [key in keyof K]: V }
for (const [key, value] of this.entries()) {
result[key] = value //Error:Type 'K' cannot be used to index type '{ [key in keyof K]: V; }'.ts(2536)
}
return result
}
}
cheia și valoarea au tipurile K și V, respectiv.
Nu înțeleg clar mesajul de eroare, ar fi bine dacă mi l-ai explica.
Am încercat, de asemenea, să-l fac mai ușor.
class Group<K, V> extends Map<K, V> {
toObject() {
const result = {}
for (const [key, value] of this.entries()) {
result[key] = value //Error:Type 'K' cannot be used to index type '{}'.ts(2536)
}
return result
}
}
Răspuns #1
Problema principală aici este că foloseai keyof K în loc de doar K. Operatorul de tip keyof îți oferă uniunea cheilor (adesea tipuri literale precum "a") ale tipului la care se aplică. keyof {a: 1, b: 2, c: 3} este "a" | "b" | "c". Deci keyof K ar fi orice chei te-ai aștepta ca o valoare de tip K să aibă. Dacă K este ceva de genul string, atunci keyof K este ceva de genul keyof string care este:
type KeyofString = keyof string;
/* type KeyofString = number | typeof Symbol.iterator | "toString" | "charAt" |
"charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" |
"match" | "replace" | "search" | "slice" | "split" | "substring" |
"toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" |
"trim" | "length" | "substr" | "valueOf" | "codePointAt" | "includes" |
"endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" |
"blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" |
"link" | "small" | "strike" | "sub" | "sup" | "padStart" | "padEnd" */
Decât dacă vrei ca result să aibă chei precum "toUpperCase" sau "trim", atunci keyof K nu este corect.
Dacă result[key] are sens și dacă key este de tip K, atunci vrei un tip în care poți indexa cu chei de tip K. Dacă pui o valoare value de tip V în acea proprietate, atunci vrei un tip ale cărui valori sunt de tip V. Tipul mapat {[P in K]: V} este exact un astfel de tip; cheile sunt K iar valorile sunt V. Acest tip de tip mapat, unde tipurile valorilor nu depind de cheile specifice, este adesea numit „record” și este folosit atât de des încât există un tip utilitar Record<K, V> oferit în acest scop.
Să folosim atunci Record<K, V>:
class Group<K, V> extends Map<K, V> {
toObject() {
const result = {} as Record<K, V>; // error!
// -----------------------> ~
// Type 'K' does not satisfy the constraint 'string | number | symbol'
for (const [key, value] of this.entries()) {
result[key] = value // okay
}
}
}
Deci, asta rezolvă eroarea result[key]=value, dar introduce o eroare nouă. Tipul Record<K, V> este o eroare în K, unde K nu satisface o constrângere de a fi un tip de cheie, string | number | symbol. Rețineți că string | number | symbol este folosit suficient de des încât există și un tip utilitar PropertyKey oferit pentru a fi un alias scurt pentru acesta.
Și asta este cu adevărat o problemă dacă Group<K, V> tău poate avea într-adevăr un tip arbitrar pentru K. Un obiect Map în JavaScript poate ține „chei” de orice tip, în timp ce un obiect simplu, cum este result, poate ține doar chei de tip PropertyKey. De exemplu, poți folosi obiecte Date ca „chei” pentru un Map, dar nu ca chei pentru un obiect simplu JavaScript:
const date = new Date();
const m = new Map<Date, string>();
m.set(date, "hello"); // okay
const o: any = {};
o[date] = "hello"; // error
Deci, dacă Group<K, V> are într-adevăr nevoie de o valoare de tip Record<K, V>, atunci asta înseamnă că trebuie să restricționezi K astfel încât compilatorul să permită existența new Group<K, V> doar atunci când K este suficient de asemănător cu o cheie:
class Group<K extends PropertyKey, V> extends Map<K, V> {
toObject() {
const result = {} as Record<K, V>; // okay
for (const [key, value] of this.entries()) {
result[key] = value // okay
}
}
}
Acum totul se compilează conform dorinței.
Link de teren de joacă către cod
