TypeScript is a language based on JavaScript that adds static type-checking along with type syntax. Static type-checking lets us know about problems with our code before we try to run it by reporting errors if we do something questionable.
Yesterday, 5th November, a new version of TypeScript was released. The new features include:
- Optional Chaining
- Nullish Coalescing
- Assertion Functions
- Better Support for never-Returning Functions
- --declaration and --allowJs
- More Recursive Type Aliases
- The useDefineForClassFields Flag and The declare Property Modifier
- Build-Free Editing with Project References
- Uncalled Function Checks
- Flatter Error Reporting
- // @ts-nocheck in Typescript Files
- Semicolon Formatter Option
- Website and Playground Updates
- Breaking Changes:
- DOM Changes
- Class Field Mitigations
- Function Truthy Checks
- Local and Imported Type Declarations Now Conflict
- API Changes
We'll be looking at Optional Chaining and Null Coalescing. Here is a comprehensive read on all the new features.
Optional Chaining
At its core, optional chaining lets us write code where we can immediately stop running some expressions if we run into a null
or undefined
. There's a new operator - ?.
for optional property accesses. When we write code like this
let x = foo?.bar.baz();
this is a way of saying that when foo
is defined, foo.bar.baz()
will be computed but when foo
is null
or undefined
, stop what we're doing and just return undefined
.
More plainly, that code snippet is the same as writing the following.
let x = (foo === null || foo === undefined) ?
undefined :
foo.bar.baz();
Note that if bar
is null
or undefined
, our code will still hit an error accessing baz
. Likewise, if baz
is null
or undefined
, we'll hit an error at the call site. ?.
only checks for whether the value on the left of it is null
or undefined
- not any of the subsequent properties.
Keep in mind that ?.
acts differenty than those &&
operations since &&
will act specially on "falsy" values (e.g. the empty string, 0
, NaN
, and, well, false
). The new operator ?.
doesn't short-circuit on valid data like 0
or empty strings.
Optional Element Access
Optional chaining includes optional element access which acts similarly to optional property accesses, but allows us to access non-identifier properties (e.g. arbitrary strings, numbers, and symbols):
/**
* Get the first element of the array if we have an array.
* Otherwise return undefined.
*/
function tryGetFirstElement<T>(arr?: T[]) {
return arr?.[0];
// equivalent to
// return (arr === null || arr === undefined) ?
// undefined :
// arr[0];
}
Optional Call
This allows to conditionally call expressions if they're not null
or undefined
.
async function makeRequest(url: string, log?: (msg: string) => void) {
log?.(`Request started at ${new Date().toISOString()}`);
// roughly equivalent to
// if (log != null) {
// log(`Request started at new ${new Date().toISOString()}`);
// }
const result = (await fetch(url)).json();
log?.(`Request finished at ${new Date().toISOString()}`);
return result;
}
The "short-circuiting" behaviour that optional chains have is limited to property accesses, calls, element accesses - it doesn't expand any further out from these expressions. In other words,
let result = foo?.bar / someComputation()
doesn't stop the division or someComputation()
call from occuring. It's equivalent to
let temp = (foo === null || foo === undefined) ? undefined : foo.bar;
let result = temp / someComputation();
That might result in dividing undefined
.
Nullish Coalescing
This introduces a new operator ??
. It is a way to "fall back" to a default value when dealing with null
or undefined
. When we write code like
let x = foo ?? bar();
this is a new way to say that the value foo
will be used when it's "present"; but when it's null
or undefined
, calculate bar()
in its place.
Again, the above code is equivalent to the following.
let x = (foo !== null && foo !== undefined) ? foo : bar();
The ??
operator can replace uses of ||
when trying to use a default value. For example, the following code snippet tries to fetch the volume that was last saved in localStorage(if it ever was); however, it has a bug because it uses ||
.
function initializeAudio() {
let volume = localStorage.voume || 0.5
// ...
}
When localStorage.volume
is set to 0
, the page will set the volume to 0.5
which is unintended. The ??
operator avoids some unintended behaviour from 0
, NaN
and ""
being treated as falsy values.
Top comments (2)
Nice one man...
Thanks!🤗