Reputation:
How can I determine whether an object x
has a defined property y
, regardless of the value of x.y
?
I'm currently using
if (typeof(x.y) !== 'undefined')
but that seems a bit clunky. Is there a better way?
Upvotes: 393
Views: 383635
Reputation: 18156
In addition to other answers I would like to suggest using the Object.hasOwn()
method for checking if the specified object has the specified property as its own property (meaning on the object itself) you can use the new Object.hasOwn()
method which is a static method which returns true if the specified object has the specified property as its own property. If the property is inherited, or does not exist, the method returns false.
const person = { name: 'dan' };
console.log(Object.hasOwn(person, 'name'));// true
console.log(Object.hasOwn(person, 'age'));// false
const person2 = Object.create({gender: 'male'});
console.log(Object.hasOwn(person2, 'gender'));// false
It is recommended to this method use over the Object.hasOwnProperty()
because it also works for objects created by using Object.create(null)
and for objects that have overridden the inherited hasOwnProperty()
method. Although it's possible to solve these kind of problems by calling Object.prototype.hasOwnProperty()
on an external object, Object.hasOwn()
overcome these problems, hence is preferred (see examples below)
let person = {
hasOwnProperty: function() {
return false;
},
age: 35
};
if (Object.hasOwn(person, 'age')) {
console.log(person.age); // true - the remplementation of hasOwnProperty() did not affect the Object
}
let person = Object.create(null);
person.age = 35;
if (Object.hasOwn(person, 'age')) {
console.log(person.age); // true - works regardless of how the object was created
}
More about Object.hasOwn
can be found here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
Browser compatibility - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn#browser_compatibility
Checking if the specified property exists on the object itself or up in the prototype chain could be verified via the in
operator as other answers suggested.
Upvotes: 3
Reputation: 73998
Unlike other examples in this thread, this implementation only asserts that object has a property for which we are checking.
const hasOwnProperty = <X extends {}, Y extends PropertyKey>(
object: X,
property: Y
): object is Record<Y, unknown> & X => {
return object.hasOwnProperty(property);
};
Here is an example of it being used to identify a branch that has the desired 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');
};
Annoyingly, the catch is that now PAID_PROJECT_FEE_TYPE_LABELS
is assumed to be:
Record<PaidProjectFeeType, unknown> & Record<"FIXED_PRICE" | "RATE", string>
i.e. You cannot return the result because a possible value of X[Y]
is unknown
. This is useful when you need to assert that object has a desired property, but you will need to add further assertions to ensure that the result is what you intend.
There is a better way, though.
We will need two utilities:
export const keys = <T extends Record<string, unknown>>(
object: T
): Array<keyof T> => {
return Object.keys(object);
};
keys
gives us a typed array of object property names.
export const includes = <C extends M, M>(
collection: readonly C[],
member: M
): member is C => {
return collection.includes(member as C);
};
includes
allows to assert that a property is a member of a read-only array. You can read more about includes in this blog post.
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');
};
In short, this approach allows us to narrow down feeType
value to the values present in keys(PAID_PROJECT_FEE_TYPE_LABELS)
, which then allows us to access the property value.
This approach works the best, with a caveat that technically keys
implementation is not run-time safe. There is (mostly theoretical) scenario where values returned runtime are different than those inferred using tsc.
Upvotes: 1
Reputation: 628
includes
Object.keys(x).includes('y');
The Array.prototype.includes()
method determines whether an array includes a certain value among its entries, returning true or false as appropriate.
and
Object.keys()
returns an array of strings that represent all the enumerable properties of the given object.
.hasOwnProperty()
and the ES6+ ?.
-optional-chaining like: if (x?.y)
are very good 2020+ options as well.
Upvotes: 6
Reputation: 1
const data = [{"b":1,"c":100},{"a":1,"b":1,"c":150},{"a":1,"b":2,"c":100},{"a":2,"b":1,"c":13}]
const result = data.reduce((r, e) => {
r['a'] += (e['a'] ? e['a'] : 0)
r['d'] += (e['b'] ? e['b'] : 0)
r['c'] += (e['c'] ? e['c'] : 0)
return r
}, {'a':0, 'd':0, 'c':0})
console.log(result)
`result` { a: 4, d: 5, c: 363 }
Upvotes: 0
Reputation:
There is a new feature on ES6+ that you can check it like below:
if (x?.y)
Actually, the interpretor checks the existence of x
and then call the y
and because of putting inside if
parentheses the coercion happens and x?.y
converted to boolean.
Upvotes: 3
Reputation: 7
Why not simply:
if (typeof myObject.myProperty == "undefined") alert("myProperty is not defined!");
Or if you expect a specific type:
if (typeof myObject.myProperty != "string") alert("myProperty has wrong type or does not exist!");
Upvotes: -3
Reputation: 342
Since question was regarding clunkiness of property checking, and one regular usecase for that being validation of function argument options objects, thought I'd mention a library-free short way of testing existence of multiple properties. Disclaimer: It does require ECMAScript 5 (but IMO anyone still using IE8 deserves a broken web).
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
Upvotes: 1
Reputation: 106412
If you are testing for properties that are on the object itself (not a part of its prototype chain) you can use .hasOwnProperty()
:
if (x.hasOwnProperty('y')) {
// ......
}
You can use the in
operator to test for properties that are inherited as well.
if ('y' in x) {
// ......
}
Upvotes: 710
Reputation:
One feature of my original code
if ( typeof(x.y) != 'undefined' ) ...
that might be useful in some situations is that it is safe to use whether x
exists or not. With either of the methods in gnarf's answer, one should first test for x
if there is any doubt if it exists.
So perhaps all three methods have a place in one's bag of tricks.
Upvotes: 6
Reputation: 828060
If you want to know if the object physically contains the property @gnarf's answer using hasOwnProperty
will do the work.
If you're want to know if the property exists anywhere, either on the object itself or up in the prototype chain, you can use the in
operator.
if ('prop' in obj) {
// ...
}
Eg.:
var obj = {};
'toString' in obj == true; // inherited from Object.prototype
obj.hasOwnProperty('toString') == false; // doesn't contains it physically
Upvotes: 96
Reputation: 28130
You can trim that up a bit like this:
if ( x.y !== undefined ) ...
Upvotes: 14