When working with data, we often have to traverse tree-like structures to find and use specific data. An example of might be retrieving the address from a user in your system
{
data: {
user: {
name: "John Doe",
age: 30,
address: {
firstLine: "19 Pound Lane",
secondLine: "Botley",
city: "Oxford",
postcode: "OX20 1RS"
}
}
}
}
To get the city
you would need to do something like this:
const city = data.user.address.city;
This is simple enough but we are making a lot of assumptions and placing a lot of trust in the data being provided. What if user
doesn't exist? We then have a problem. We will get a Uncaught TypeError
. And our app may crash!!
To provide some protection against this we have a number of options. We can nest logic or ternary statements, use logic expressions with &&
or wrap our code in try/catch blocks but this all leads to hard to read, verbose code. There are also helper functions from libraries like lodash.
// Examples
// nested logic
if(data) {
if(user) {
if(address) {
[...]
}
}
}
// nested ternary
const city = !data
? undefined
: !data.user
? undefined
[...]
// lodash
const city = _.get(data, "user.address.city", undefined);
Optional Chaining Operator - Our knight in shining armour
MDN says it best:
The optional chaining operator ?. permits reading the value of a property located deep within a chain of connected objects without having to expressly validate that each reference in the chain is valid. The ?. operator functions similarly to the . chaining operator, except that instead of causing an error if a reference is null or undefined, the expression short-circuits with a return value of undefined. When used with function calls, it returns undefined if the given function does not exist.
Js
This means that instead of having to check each property to make sure we don't break anything we can use this new operator ?
. It's so much neater!
const city = data?.user?.address?.city;
This is currently stage 3 but it can be used today with the help of a babel-plugin-proposal-optional-chaining transform.
But where does that leave us, Typescript developers?
This transform won't currently work with Typescript. The Typescript team are waiting for the proposal to be finalised and is pencilled in release in v3.7. But there is currently no release date for v3.7 so we need something in the interim.
Ts
This is where ts-optchain comes to the rescue. It is not quite as concise as the ?
syntax but supports default values, retains all your types and can be used TODAY!!
That sounds like a win to me!
Once you have installed the package:
yarn add -D ts-optchain
You will need to add the plugin to your tsconfig.json
and you will be ready to go.
// tsconfig.json
{
"compilerOptions": {
"plugins": [{ "transform": "ts-optchain/transform" }]
}
}
With this installed and configured we can access our city
property in a much more concise manner.
import { oc } from "ts-optchain";
interface User {
data?: {
user?: {
name?: string;
age?: number;
address?: {
firstLine?: string;
secondLine?: string;
city?: string;
postcode?: string;
};
};
};
}
const user: User = {
data: {
user: {
name: "John Doe",
age: 30,
address: {
firstLine: "19 Pound Lane",
secondLine: "Botley",
city: "Oxford",
postcode: "OX20 1RS"
}
}
}
};
console.log(oc(user).data.user.address.city("City not Found"));
console.log(
(user &&
user.data &&
user.data.user &&
user.data.user.address &&
user.data.user.address.city) ||
"City not Found"
);
Wow that second option:
You can see that this allows us to write much less verbose code, meaning our code will be easier to read and refactor.
Conclusion
This was a quick post to share the great things coming to Javascript in the near (hopefully) future and that they can still be used today with a little extra work. The optional chaining operator is such a great addition to the language! Start using it now.
Top comments (0)