我如何确定一个对象x是否具有定义的属性y,而不管x.y的值是多少?
我正在使用
if (typeof(x.y) !== 'undefined')
但这似乎有点笨拙。有没有更好的办法?
我如何确定一个对象x是否具有定义的属性y,而不管x.y的值是多少?
我正在使用
if (typeof(x.y) !== 'undefined')
但这似乎有点笨拙。有没有更好的办法?
当前回答
与此线程中的其他示例不同,此实现仅断言对象具有我们正在检查的属性。
const hasOwnProperty = <X extends {}, Y extends PropertyKey>(
object: X,
property: Y
): object is Record<Y, unknown> & X => {
return object.hasOwnProperty(property);
};
下面是一个使用它来标识具有所需属性的分支的示例。
const hasOwnProperty = <X extends {}, Y extends PropertyKey>(
object: X,
property: Y
): object is Record<Y, unknown> & X => {
return object.hasOwnProperty(property);
};
type PaidProjectFeeType = 'FIXED_PRICE' | 'RATE' | '%future added value';
const PAID_PROJECT_FEE_TYPE_LABELS: Record<
'FIXED_PRICE' | 'RATE',
string
> = {
FIXED_PRICE: 'Fixed Price',
RATE: 'Rate',
};
export const getPaidProjectFeeTypeLabel = (
feeType: PaidProjectFeeType
): string => {
if (hasOwnProperty(PAID_PROJECT_FEE_TYPE_LABELS, feeType)) {
PAID_PROJECT_FEE_TYPE_LABELS[feeType];
}
throw new Error('test');
};
https://tsplay.dev/m0LBOm
令人恼火的是,现在PAID_PROJECT_FEE_TYPE_LABELS被假设为:
Record<PaidProjectFeeType, unknown> & Record<"FIXED_PRICE" | "RATE", string>
也就是说,你不能返回结果,因为X[Y]的可能值是未知的。当您需要断言对象具有所需的属性时,这很有用,但您将需要添加进一步的断言以确保结果是您想要的。
不过,有一个更好的办法。
我们需要两个工具:
export const keys = <T extends Record<string, unknown>>(
object: T
): Array<keyof T> => {
return Object.keys(object);
};
Keys为我们提供了对象属性名的类型化数组。
export const includes = <C extends M, M>(
collection: readonly C[],
member: M
): member is C => {
return collection.includes(member as C);
};
Includes允许断言某个属性是只读数组的成员。你可以在这篇博客文章中阅读更多关于includes的内容。
export const keys = <T extends Record<string, unknown>>(
object: T
): Array<keyof T> => {
return Object.keys(object);
};
export const includes = <C extends M, M>(
collection: readonly C[],
member: M
): member is C => {
return collection.includes(member as C);
};
type PaidProjectFeeType = 'FIXED_PRICE' | 'RATE' | '%future added value';
const PAID_PROJECT_FEE_TYPE_LABELS: Record<
'FIXED_PRICE' | 'RATE',
string
> = {
FIXED_PRICE: 'Fixed Price',
RATE: 'Rate',
};
export const getPaidProjectFeeTypeLabel = (
feeType: PaidProjectFeeType
): string => {
if (includes(keys(PAID_PROJECT_FEE_TYPE_LABELS), feeType)) {
return PAID_PROJECT_FEE_TYPE_LABELS[feeType];
}
throw new Error('test');
};
https://tsplay.dev/N7gLDN
简而言之,这种方法允许我们将feeType值缩小到键中存在的值(PAID_PROJECT_FEE_TYPE_LABELS),然后允许我们访问属性值。
这种方法效果最好,但需要注意的是,从技术上讲,键的实现不是运行时安全的。在运行时返回的值与使用tsc推断的值不同的情况下(主要是理论性的)。
其他回答
除了其他答案,我想建议使用object . hasown()方法来检查指定的对象是否有指定的属性作为自己的属性(在对象本身上的意思),你可以使用新的object . hasown()方法,这是一个静态方法,如果指定的对象有指定的属性作为自己的属性,它会返回true。如果该属性被继承,或不存在,则该方法返回false。
Const person = {name: 'dan'}; console.log(对象。hasOwn(person, 'name')) console.log(对象。hasOwn(person, 'age')) const person2 =对象。创建({性别:男性的}); console.log(对象。hasOwn(person2, 'gender'))
建议在Object.hasOwnProperty()方法上使用此方法,因为它也适用于使用Object.create(null)创建的对象以及覆盖继承的hasOwnProperty()方法的对象。虽然可以通过在外部对象上调用object .prototype. hasownproperty()来解决这类问题,但object . hasown()克服了这些问题,因此是首选的(参见下面的示例)
Let person = { hasOwnProperty: function() { 返回错误; }, 年龄:35 }; 如果对象。hasOwn(人,年龄)){ console.log (person.age);// true - hasOwnProperty()的补充不影响对象 }
let person = Object.create(null); 的人。年龄= 35岁; 如果对象。hasOwn(人,年龄)){ console.log (person.age);// true -无论对象是如何创建的都有效 }
更多关于Object的信息。hasOwn可以在这里找到:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
浏览器兼容性- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn#browser_compatibility
检查指定的属性是否存在于对象本身或原型链中,可以通过in操作符验证,就像其他答案所建议的那样。
你可以像这样修剪一下:
if ( x.y !== undefined ) ...
对象具有以下属性:
如果你正在测试对象本身的属性(不是原型链的一部分),你可以使用.hasOwnProperty():
if (x.hasOwnProperty('y')) {
// ......
}
对象或其原型具有一个属性:
您还可以使用in操作符测试继承的属性。
if ('y' in x) {
// ......
}
与此线程中的其他示例不同,此实现仅断言对象具有我们正在检查的属性。
const hasOwnProperty = <X extends {}, Y extends PropertyKey>(
object: X,
property: Y
): object is Record<Y, unknown> & X => {
return object.hasOwnProperty(property);
};
下面是一个使用它来标识具有所需属性的分支的示例。
const hasOwnProperty = <X extends {}, Y extends PropertyKey>(
object: X,
property: Y
): object is Record<Y, unknown> & X => {
return object.hasOwnProperty(property);
};
type PaidProjectFeeType = 'FIXED_PRICE' | 'RATE' | '%future added value';
const PAID_PROJECT_FEE_TYPE_LABELS: Record<
'FIXED_PRICE' | 'RATE',
string
> = {
FIXED_PRICE: 'Fixed Price',
RATE: 'Rate',
};
export const getPaidProjectFeeTypeLabel = (
feeType: PaidProjectFeeType
): string => {
if (hasOwnProperty(PAID_PROJECT_FEE_TYPE_LABELS, feeType)) {
PAID_PROJECT_FEE_TYPE_LABELS[feeType];
}
throw new Error('test');
};
https://tsplay.dev/m0LBOm
令人恼火的是,现在PAID_PROJECT_FEE_TYPE_LABELS被假设为:
Record<PaidProjectFeeType, unknown> & Record<"FIXED_PRICE" | "RATE", string>
也就是说,你不能返回结果,因为X[Y]的可能值是未知的。当您需要断言对象具有所需的属性时,这很有用,但您将需要添加进一步的断言以确保结果是您想要的。
不过,有一个更好的办法。
我们需要两个工具:
export const keys = <T extends Record<string, unknown>>(
object: T
): Array<keyof T> => {
return Object.keys(object);
};
Keys为我们提供了对象属性名的类型化数组。
export const includes = <C extends M, M>(
collection: readonly C[],
member: M
): member is C => {
return collection.includes(member as C);
};
Includes允许断言某个属性是只读数组的成员。你可以在这篇博客文章中阅读更多关于includes的内容。
export const keys = <T extends Record<string, unknown>>(
object: T
): Array<keyof T> => {
return Object.keys(object);
};
export const includes = <C extends M, M>(
collection: readonly C[],
member: M
): member is C => {
return collection.includes(member as C);
};
type PaidProjectFeeType = 'FIXED_PRICE' | 'RATE' | '%future added value';
const PAID_PROJECT_FEE_TYPE_LABELS: Record<
'FIXED_PRICE' | 'RATE',
string
> = {
FIXED_PRICE: 'Fixed Price',
RATE: 'Rate',
};
export const getPaidProjectFeeTypeLabel = (
feeType: PaidProjectFeeType
): string => {
if (includes(keys(PAID_PROJECT_FEE_TYPE_LABELS), feeType)) {
return PAID_PROJECT_FEE_TYPE_LABELS[feeType];
}
throw new Error('test');
};
https://tsplay.dev/N7gLDN
简而言之,这种方法允许我们将feeType值缩小到键中存在的值(PAID_PROJECT_FEE_TYPE_LABELS),然后允许我们访问属性值。
这种方法效果最好,但需要注意的是,从技术上讲,键的实现不是运行时安全的。在运行时返回的值与使用tsc推断的值不同的情况下(主要是理论性的)。
既然问题是关于属性检查的笨拙,一个常规的用例是函数参数选项对象的验证,我认为我应该提到一种无库的测试多个属性是否存在的简短方法。 免责声明:它确实需要ECMAScript 5(但在我看来,任何仍在使用IE8的人都应该拥有一个破碎的网络)。
function f(opts) {
if(!["req1","req2"].every(opts.hasOwnProperty, opts)) {
throw new Error("IllegalArgumentException");
}
alert("ok");
}
f({req1: 123}); // error
f({req1: 123, req2: 456}); // ok