Borislav Hadzhiev
Mon Feb 28 2022·4 min read
Photo by Alexandre Croussette
To check if a property exists in an object in TypeScript:
undefined
, it exists in the object.type Employee = { name?: string; department?: string; country?: string; }; const emp: Employee = {}; // ✅ Explicitly checking if (emp.department !== undefined) { console.log(emp.department.toLowerCase()); // now string } // ✅ Using optional chaining console.log(emp.department?.toLowerCase());
Note that we used a question mark to set the properties in the Employee
type
to
optional.
undefined
in the object or a value of type string
.If you don't have all of the names of properties in the object in advance, you can use an index signature.
type Employee = { [key: string]: string; }; const emp: Employee = {}; // ✅ Explicitly checking if (emp.department !== undefined) { console.log(emp.department.toLowerCase()); // now string } // ✅ Using optional chaining console.log(emp.department?.toLowerCase());
The {[key: string]: string}
syntax in the example is an index signature and is
used when we don't know all of the object's properties ahead of time, but know
the shape of the values.
In either scenario, you can only check if a property exists in a TypeScript object, if the property is compatible with the object's type.
type Employee = { name?: string; department?: string; country?: string; }; const emp: Employee = {}; // ⛔️ Property 'randomkey' does not // exist on type 'Employee'.ts(2339) if (emp.randomkey !== undefined) { }
The Employee
type in the example does not have a randomkey
property, so we
aren't able to check if it exists in the object.
The best way to check for a key's existence in TypeScript is to explicitly check
if accessing the specific key in the object returns a value of undefined
.
type Employee = { name?: string; department?: string; country?: string; }; const emp: Employee = {}; // 👇️ (property) department?: string | undefined emp.department // ✅ Explicitly checking if (emp.department !== undefined) { console.log(emp.department.toLowerCase()); }
The department
property has a type of string
or undefined
because it's
marked as optional.
However, if the if
statement, its type is string
. This is called a
type guard in
TypeScript.
We are able to use any string-specific built-in methods in the if
statement,
because TypeScript knows that if the department
property is not undefined
,
then it is a string
.
if
statement would not run if the property is explicitly set to undefined
in the object.Alternatively, you can use optional chaining.
Use optional chaining (?.) to check if a property exists in a TypeScript
object, e.g. emp.myProperty?.toLowerCase()
. If the property exists in the
object, the optional chaining operator will return the properties value,
otherwise it short-circuits returning undefined
.
type Employee = { address?: { country?: string; city?: string; }; }; const emp: Employee = {}; // ✅ Using optional chaining console.log(emp.address?.country?.toLowerCase()); console.log(emp.address?.city?.toLowerCase()); if (emp.address?.country !== undefined) { // ✅ Now emp.address.country is string console.log(emp.address.country.toLowerCase()); }
The optional chaining (?.) operator short-circuits if the reference is nullish
(null
or undefined
) instead of throwing an error.
You can also use this approach to check for the existence of deeply nested properties in an object.
You might see examples online that use the in
operator or the hasOwnProperty
method to check for a key's existence in an object.
type Employee = { name?: string; department?: string; country?: string; }; const emp: Employee = {}; if ('department' in emp) { // 👇️ (property) department?: string | undefined console.log(emp.department); } if (emp.hasOwnProperty('department')) { // 👇️ (property) department?: string | undefined console.log(emp.department); }
The
in operator
returns true
if the specified key is in the object or its prototype chain.
However, note that the type of the department
property in the if
block is
still string | undefined
.
department
key in the object to have a value of undefined
.This would work in a different way if you were using an index signature like
{[key: string]: string}
.
type Employee = { [key: string]: string; }; const emp: Employee = {}; if ('department' in emp) { // 👇️ emp.department is string console.log(emp.department); } if (emp.hasOwnProperty('department')) { // 👇️ emp.department is string console.log(emp.department); }
The index signature {[key: string]: string}
means that when the object is
indexed with a key that is a string, it will always return a value of type
string
.
This is why the in
operator and the hasOwnProperty
methods are able to serve
as type guards and determine the type of the department
property to be a
string in the if
statements.
The
hasOwnProperty
method is different from the in
operator, because it checks if the property
exists directly on the object (excluding its prototype chain).
type Employee = { [key: string]: string; }; const emp: Employee = {}; if (emp.hasOwnProperty('department')) { // 👇️ emp.department is string console.log(emp.department); }
If ESLint is giving you an error, you can get around it by calling the method on
the Object.prototype
.
type Employee = { [key: string]: string; }; const emp: Employee = {}; if (Object.prototype.hasOwnProperty.call(emp, 'department')) { // 👇️ emp.department is string console.log(emp.department); }
Which approach you pick is a matter of personal preference. I'd go with
explicitly checking if accessing the property in the object returns a value of
undefined
as it is the easiest to read and the most explicit approach.