我有一个用一个参数调用的函数。此参数是对象。此函数返回另一个对象,其中一个属性是传递给函数对象的值。当我尝试使用obj [key]表示法检索对象的值时遇到问题。
我确实知道,密钥必须是正确的类型,才能像这样使用。我不能使用简单的键作为字符串,如果在接口中我没有[key:string]:any。但这不是问题。我的键是作为传递对象中的键的字符串的并集:'user'| '名称'等
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>
我的功能如下:
public static getArrayOfObjects(
queryParameters: Twitter<string> | Facebook<string>,
) {
const keys = Object.keys(queryParameters) as TwFbObjKeys[];
return keys.map((paramId) => ({
paramId,
value: queryParameters[paramId],
}));
}
我希望使用类型为'name'|的paramId。 “性别” | 'appName'| ... 作为具有此键的对象的键不会引发错误。但不幸的是我有错误:
TS7053:元素隐式地具有“ any”类型,因为类型为“ name”的表达式| “性别” | 'appName'| ...不能用于索引Twitter |脸书 类型“ Twitter”上不存在属性“名称” Facebook
我正在和它战斗几个小时。知道我该如何解决吗?
答案 0 :(得分:3)
最好将函数参数queryParameters
定义为具有约束T
的泛型类型<T extends Twitter<string> | Facebook<string>>
而不是并集类型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],
}));
}
说明
通过使用泛型,您可以保留queryParameters
的传入参数的类型,并确保它是Twitter<string> | Facebook<string>
的子类型。类型为paramId
的{{1}}现在可以访问keyof T
的属性。
使用并集类型queryParameters
时,Twitter<string> | Facebook<string>
仍未确定,并且在函数体中可能仍为queryParameters
或Twitter<string>
。在您的情况下,这会导致Facebook<string>
运算符和属性访问出现问题,因为您只能访问给定联合类型的所有成分的 common 属性键。并且keyof
和Twitter<string>
没有通用属性。
为进一步说明问题,您可能打算将Facebook<string>
定义为
TwFbObjKeys
但是,这不能单独解决问题。示例:
// "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>
借助通用类型,我们可以使用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"]
安全地访问传入的keyof T
键。
答案 1 :(得分:1)
我认为您的问题出在那一行
type TwFbObjKeys = keyof Twitter<string> | Facebook<string>
// is of type type TwFbObjKeys = "name" | "user" | "sex" | Facebook<string>
您可能想要
type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>
// of type type TwFbObjKeys = "name" | "user" | "sex" | "appName" | "whatever" | "imjustanexapmle"
答案 2 :(得分:1)
(假设您的意思是type TwFbObjKeys = keyof Twitter<string> | keyof Facebook<string>
)
Typescript引发错误,因为paramId
的类型包含两个接口的所有键:
“名称” | '用户'| “性别” | 'appName'| “随便什么” | 'imjustanexapmle'
但是queryParameters
只能是任一接口的类型:
“名称” | '用户'| “性别”
或
“ appName” | “随便什么” | 'imjustanexapmle'
要解决此问题,您应该将两种类型分开:
interface Twitter<T> {
name: T;
user: T;
sex: T extends string ? boolean : string;
}
interface Facebook<T> {
appName: T;
whatever: T;
imjustanexapmle: T;
}
function isTwitter<T>(val: Twitter<T> | Facebook<T>): val is Twitter<T> {
return (
'name' in (val as Twitter<T>) &&
'user' in (val as Twitter<T>) &&
'sex' in (val as Twitter<T>)
)
}
function isFacebook<T>(val: Twitter<T> | Facebook<T>): val is Facebook<T> {
return (
'appName' in (val as Twitter<T>) &&
'whatever' in (val as Twitter<T>) &&
'imjustanexapmle' in (val as Twitter<T>)
)
}
function getValue<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
function getArrayOfObjects(
queryParameters: Twitter<string> | Facebook<string>,
) {
if (isTwitter(queryParameters)) { // if queryParameters is of type Twitter<string>
const keys = Object.keys(queryParameters);
return keys.map((paramId) => ({
paramId,
value: getValue(queryParameters, paramId as keyof Twitter<string>)
}));
} else if (isFacebook(queryParameters)) { // if queryParameters is of type Facebook<string>
const keys = Object.keys(queryParameters);
return keys.map((paramId) => ({
paramId,
value: getValue(queryParameters, paramId as keyof Facebook<string>)
}));
} else { // if queryParameters is invalid
throw new Error("invalid queryParameters!")
}
}