I think the TypeScript 3.7 release is enormous. I have waited a very, very long time to have optional chaining having tasted it briefly on a C# contract a few years ago.
video by Carl Rippon
One of the most significant pain points that most of us have in the JavaScript/TypeScript world is continuously checking variables or expressions for null
or undefined
. The example below is TypeScript 3.7 and illustrates what a game-changer this release is. We can finally drastically stop the amount of nonsense code we have to write with the new feature.
class Student { constructor(public name: string) {} discipline?: Discipline; printDetails() { return ` name: ${this.name} discipline: this.discipline?.name || 'Not set'; `; } } class Discipline { student?: Student; constructor(public name: string) { } } class College { constructor(public name: string) { } disciplines?: Discipline[]; } class City { constructor(public name: string) { } college?: College; } class University { constructor(public name: string) { } city?: City; get college(): College | undefined { return this?.city?.college; } addDisciplines(...disciplines: Discipline[]) { if (!this.city?.college?.disciplines) { return; } this.city.college.disciplines.push(...disciplines) } } const university = new University("Queen's University Belfast"); const city = new City("Belfast"); const computerScience = new Discipline("Computer Science"); const economics = new Discipline("Economics"); const college = new College("Da College"); const student = new Student("Brian Cant"); university.city = city; university.city.college; university.addDisciplines(computerScience, economics); const firstStudent = university.city?.college?.disciplines?.[0]?.student; // Student | undefined console.log(firstStudent?.printDetails())
Here is a playground with a working example of the above code snippet.
Line 1 contains a Student
class definition which contains an optional discipline
property that might have an undefined
or null
value. A printDetails
method exists on line 6 that involves access with an optional property.
printDetails() { return ` name: ${this.name} discipline: this.discipline?.name || 'Not set'; `; }
You specify optional chaining by placing a ?
question mark after the optional property value on which you wish to call the property, method or even subscript (more on this later) to force the unwrapping of its value. If during the unwrapping of the property or method a null
value or an undefined
value is found, undefined
is returned. undefined
is always returned no matter if the underlying value is null
or undefined
.
Below is some of the horrible code we would tediously write before this exquisite release.
printDetails() { return ` name: ${this.name} discipline: this.discipline && this.discipline.name ? this.discipline.name : 'Not set'; `; }
Where optional chaining glistens is when dealing with deeply nested objects on line 3 of the code example below or line 63 of the first code example:
university.addDisciplines(computerScience, economics); const firstStudent = university.city?.college?.disciplines?.[0]?.student; // Student | undefined console.log(firstStudent?.printDetails())
We would previously have to do some pretty disgusting things to access such a deep hierarchy, but we can now optionally chain our way to a much better existence with TypeScript 3.7.
You can use optional chaining to try to retrieve a value from a subscript on an optional value and to check whether that subscript call is successful.
const firstStudent = university.city?.college?.disciplines?.[0]?.student;
Instances of the Discipline
class can have an array of students. When you access a subscript on an optional value through optional chaining, you place the question mark before the subscript’s brackets, not after. The optional chaining question mark always follows immediately after the part of the expression that is optional.
I think this is massive, we will write significantly less code that checks for the existence of things. I cannot contain my excitement at this release.
Another feature I have experienced previously in C# is the null coalescing operator (??
) that simplifies checking for null or undefined values.
const a: number | null = null; const b = a ?? 42; console.log(b); // 42
||
?The first time I saw this in TypeScript, I shouted ‘what about ||
?’ Below is the same example with the same result.
const a: number | null = null; const b = a || 42; console.log(b); // 42
Where this example falls apart is with JavaScript’s truthy and falsy shenanigans.
const a: number | null = 0; const b = a || 42; console.log(b); // 42
Unfortunately 0
returns false when used in an expression with JavaScript so 42
will get to the console which is incorrect.
The coalescing operator comes to the rescue in this situation.
const a: number | null = 0; const b = a ?? 42; console.log(b); // 0
I am going to stop using the shortcircuit or ||
operator when TypeScript 3.7 gets released and instead use the beautiful ??
operator.
I have waited a long time for these changes, and I think the TypeScript official announcement does its usual job of underselling the great new features.
I also hate the code examples they use:
// Before if (foo && foo.bar && foo.bar.baz) { // ... } // After-ish if (foo?.bar?.baz) { // ... }
I think this illustrates the disconnect between the real world and the TypeScript developers. foo
, bar
,and baz
are just about the worst examples to use for anything. I cannot think of anything tangible with these made up properties.
I think there should be much more made of this release. It will make writing TypeScript significantly less annoying.
Please leave feedback on the comments below.
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]
8 Replies to "New in TypeScript 3.7"
pretty neat. now, what about interface extension methods? Just like C# has.
I need interface static methods
Great post!
So typescript is going to have pretty much every feature C# has. (what a coincidence, they’re designed by the same person)
The optional chaining operator just tells me that I am going to show up at client sites where every single dot in the application is preceded by a question mark, the same way that async permeates through the code.
It tells me that nobody is actually going to know anything about the data that runs through the system, and it’s going to hide bugs that are ridiculous to solve, because they could have happened at any `?.` upstream from where your data was supposed to be.
Moreover, this affects all downstream systems, as well.
I’m not saying that 10,000 null checks is good, either. What I am saying is that we are programming backwards. What I am saying is that we should have systems that are hermetically sealed, and operate only on types that are known, not types where every member is optional/nullable. If you can’t build an instance of a type with the pieces you have been given (plus any reasonable defaults), then you should take a different path in code, rather than passing in a tree that is garnished with nulls.
The invention/inclusion of null in programming languages was Tony Hoare’s billion dollar mistake, and instead of thinking about how we can program without it, we are making it easier to make everything null, at all times, without crashing.
However, the null coalescing operator is a good tool for building those known and trusted data types from untrusted data sources (server responses, database fields, localStorage, user input), *if* the field you are settling has a valid default/empty/identity state. Optional chaining would be fine to use in the case where you were grabbing an expected value from an unknown data source, and using the coalescing operator to set the default, when building a closed system of well-defined types… but we all know that isn’t how it’s going to be used.
Personally I think the worst thing about their announcement blog post is the dubious formatting of ternary operators. I mean, everybody knows that you’re supposed to format them
“`
var result = condition
? first_expression()
: second_expression()
“`
I believe they are just adding features ahead of their official adoption into javascript. Example: optional chaining was not added to typescript until it was stage 3 in the tc39:
https://github.com/tc39/proposal-optional-chaining
Good point