Last updated: Feb 26, 2024
Reading timeยท3 min
The question mark dot (?.) syntax is called optional chaining in TypeScript
and is like using dot notation to access a nested property of an object, but
instead of causing an error if the reference is nullish, it short-circuits
returning undefined
.
type Person = { address?: { country?: string; }; name?: { first?: string; }; }; const person: Person = { address: { country: 'Chile', }, }; console.log(person.address?.country); // ๐๏ธ "Chile" console.log(person.name?.first); // ๐๏ธ undefined
We created a type where the address
and name
properties are optional.
The first console.log
statement uses the
optional chaining (?.)
operator to access the address.country
property on the person
object.
const person: Person = { address: { country: 'Chile', }, }; console.log(person.address?.country); // ๐๏ธ "Chile"
The address.country
property exists on the person
object, so its value gets
logged to the console.
The second example uses the optional chaining (?.) operator to access the
name.first
property on the person
object.
However, the property doesn't exist on the object, so the optional chaining (?.)
operator returns undefined
.
const person: Person = { address: { country: 'Chile', }, }; console.log(person.name?.first); // ๐๏ธ undefined
null
or undefined
) reference, the optional chaining operator (?.) short-circuits returning undefined
instead of throwing an error.If we didn't use the optional chaining (?.) operator, we would have gotten an
error because we're trying to access a property on an undefined
value.
type Person = { address?: { country?: string; }; name?: { first?: string; }; }; const person: Person = { address: { country: 'Chile', }, }; // โ๏ธ ERROR: Cannot read properties of undefined (reading 'first') console.log(person.name.first); // ๐๏ธ undefined
We didn't use the optional chaining (?.) operator to short-circuit in case the
name
property was not set, so an error occurred.
You might have used the logical AND (&&) operator to get around this in the past.
type Person = { address?: { country?: string; }; name?: { first?: string; }; }; const person: Person = { address: { country: 'Chile', }, }; console.log(person.name && person.name.first); // ๐๏ธ undefined
We used the
logical AND (&&)
operator to check if accessing the name
property on the person
object
returns a truthy value.
If it does, we try to access the first
property on the name
object.
null
or undefined
.The optional chaining operator can also be used to access an array element at a
specific index or short-circuit if the reference is null
or undefined
.
type Person = { numbers?: { low?: number[]; }; }; const person: Person = {}; console.log(person.numbers?.low?.[0]); // ๐๏ธ undefined
The low
property could be undefined
, so trying to access the array element
at index 0
on an undefined
value would throw an error unless we use the
optional chaining (?.) operator to short-circuit if the reference is
undefined
.
If the property is populated on the object, the optional chaining operator (?.) returns the specified value in the array.
type Person = { numbers?: { low?: number[]; }; }; const person: Person = { numbers: { low: [1, 2, 3], }, }; console.log(person.numbers?.low?.[0]); // ๐๏ธ 1
The optional chaining (?.) operator can also be used to call a function if its
value is not equal to null
or undefined
.
function logger(callback?: (msg: string) => void) { callback?.('hello'); } logger(console.log); // ๐๏ธ "hello"
Trying to call the callback
parameter would cause an error if it isn't
provided, so we used the optional chaining (?.) operator to check if the
reference is not null
or undefined
before calling the function.
If the parameter is not provided, the operator will just short-circuit returning
undefined
instead of calling the function.
You can learn more about the related topics by checking out the following tutorials: