Typescript: en typ som är en union av objektnycklar kan inte användas som en nyckel för detta objekt
Jag har en funktion som kallas med ett parameter. Detta parameter är ett objekt. Denna funktion returnerar ett annat objekt, vars en egenskap är värdet av det objekt som skickades till funktionen. Jag har problem när jag försöker hämta värdet av objektet, med hjälp av obj[key]-notation.
Jag förstår, den nyckeln måste vara av rätt typ för att kunna användas på det här sättet. Jag kan inte använda en enkel nyckel som en sträng, om jag i gränssnittet inte har [key: string]: any. Men det är inte problemet. Min nyckel är en union av strängar som var nycklar i det passerade objektet: 'user' | 'name' osv.
interface Twitter<T> {
name: T;
user: T;
sex: T extends string ? boolean : string;
}
interface Facebook<T> {
appName: T;
whatever: T;
imjustanexapmle: T;
}
type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>
Min funktion ser ut så här:
public static getArrayOfObjects(
queryParameters: Twitter<string> | Facebook<string>,
) {
const keys = Object.keys(queryParameters) as TwFbObjKeys[];
return keys.map((paramId) => ({
paramId,
value: queryParameters[paramId],
}));
}
Jag skulle förvänta mig att använda paramId som är av typen: 'name' | 'sex' | 'appName' | ... som en nyckel för ett objekt som har dessa nycklar, inte skulle ge ett fel. Men tyvärr får jag ett fel:
TS7053: Elementet har implicit typen 'any' eftersom uttryck av typen 'name' | 'sex' | 'appName' | ... inte kan användas för att indexera typen Twitter | Facebook. Egenskapen 'name' finns inte på typen Twitter | Facebook
Jag har kämpat med det i några timmar nu. Har du någon idé om hur jag kan lösa det?
Svar #1
Definiera hellre funktionsparametern queryParameters som generisk typ T med begränsning <T extends Twitter<string> | Facebook<string>> istället för en unionstyp Twitter<string> | Facebook<string>:
function getArrayOfObjects<T extends Twitter<string> | Facebook<string>>(
queryParameters: T
) {
const keys = Object.keys(queryParameters) as (keyof T)[];
return keys.map((paramId) => ({
paramId,
value: queryParameters[paramId],
}));
}
Lekplats
Förklaringar
Genom att använda generics behåller du typen av det skickade argumentet för queryParameters och säkerställer bara att det kommer att vara en subtyp av Twitter<string> | Facebook<string>. paramId med typen keyof T kan nu komma åt queryParameters-egenskaper.
Med en unionstyp Twitter<string> | Facebook<string> skulle queryParameters förbli obestämd och kunde fortfarande vara Twitter<string> eller Facebook<string> i funktionskroppen. Det orsakar problem med keyof-operatorn och åtkomst till egenskaper i ditt fall, eftersom du endast kan komma åt gemensamma egenskapsnycklar för alla beståndsdelar som ges av unionstypen. Och det finns inga gemensamma egenskaper för Twitter<string> och Facebook<string>.
För att illustrera problemet lite mer, menade du förmodligen att definiera TwFbObjKeys som
// "name" | "user" | "sex" | "appName" | "whatever" | "imjustanexapmle"
type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>
// not this: keyof only applies to Twitter<string> here: "name" | "user" | "sex" | Facebook<string>
type TwFbObjKeys_not = keyof Twitter<string> | Facebook<string>
Men det skulle inte lösa problemet på egen hand. Exempel:
declare const queryParameters: Twitter<string> | Facebook<string>
// type keysOfQueryParameters = never
type keysOfQueryParameters = keyof typeof queryParameters
// ok, let's try to define all union keys manually
type TwFbObjKeys = "name" | "user" | "sex" | "appName" | "whatever" | "imjustanexapmle"
declare const unionKeys: TwFbObjKeys
// error: doesn't work either
queryParameters[unionKeys]
// this would work, if both Twitter and Facebook have "common" prop
queryParameters["common"]
Lekplats
Med hjälp av en generisk typ kan vi använda keyof T för att säkert komma åt de queryParameters-nycklar som skickats in.
Svar #2
Jag tror att ditt problem kommer från den raden
type TwFbObjKeys = keyof Twitter<string> | Facebook<string>
// is of type type TwFbObjKeys = "name" | "user" | "sex" | Facebook<string>
du vill förmodligen
type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>
// of type type TwFbObjKeys = "name" | "user" | "sex" | "appName" | "whatever" | "imjustanexapmle"
