DEV Community

Cover image for How TypeScript 3.7 Helps Quality
Matt Eland
Matt Eland Subscriber

Posted on • Originally published at killalldefects.com on

How TypeScript 3.7 Helps Quality

In this article I’ll go over a few key highlights from the recent release of TypeScript 3.7. I’ll be looking at things through the lens of how they impact software and code quality, because let’s face it – that’s kind of my jam.

I’ve been following TypeScript releases for some time now. I’ve seen the last few come and go without anything that significantly impacts my day to day life. A minor release like 3.7 doesn’t sound very big or important, but this one is huge in the way it can improve code quality.

While TypeScript 3.7 has a surprising number of things in it, I’ll be writing specifically about the following changes:

  • Optional Chaining
  • Nullish Coalescing
  • Assertion Functions
  • The Declare Keyword
  • Uncalled Function Checks

Let’s get started.

Optional Chaining

Optional chaining is a form of syntax to short circuit evaluations in the event that something is null or undefined.

TypeScript introduces the ?. operator to optionally invoke something on the condition that the object exists.

Take a look at the comparison below between the old and new way of doing things:

We’ve had optional chaining in C# in the .NET world for some time, and I’m a huge fan of it.

What I love about this is that:

  • It makes syntax incredibly brief, but very readable
  • It makes it easy to check for nulls

Both of these things help a lot with both code and software quality. If I’m reviewing code and I’m not being distracted by extra syntax for if blocks, I’m able to focus on the code that actually matters.

Similarly, if I’m a developer working on a method that’s longer than it should be, I might get lazy and make an assumption that a variable has been checked for null already. This sounds silly, but I’ve felt the temptation myself of not wanting to get out of the flow and go up a line to add a null check.

Being able to quickly and conditionally check for null is going to help quality more than you might initially think.

Nullish Coalescing

Nullish coalescing refers to the use of the ?? operator in evaluating things that could potentially be null or undefined.

First of all, the term ‘nullish’ makes me laugh because it’s so incredibly appropriate for JavaScript concepts.

JavaScript needs this distinction because it has the concept of null which is separate to but related to undefined. The distinction of course, is something that is null is explicitly nothing but something undefined has literally not been defined to have any value. The perils of dynamic languages, I suppose.

For example of nullish coalescing take a look at the following before and after snippet:

As you can see, using nullish coalescing is very clean and simple compared to the ternary (?) operator of equivalent if checks with assignments.

What I like about this is similar to what I like about optional chaining – it helps you focus on the code that actually matters.

If we as engineers can eliminate extra noise in our code and syntax, we’re going to spot defects easier and earlier.

Assertion Functions

Assertion functions I’m more on the fence about. Essentially they are functions which, if called without error, have asserted something to TypeScript’s internal type interpreting code. This in turn allows the compiler to catch more specific issues based on the facts now proved to be true.

Let’s look at an example:

Here we have a getStandardFixedNumberString function that takes in a value that is known to either be a string or a number. Since toFixed is not available on a string type, this code is not normally permissible.

The assertIsNumber function defines an assertion clause that essentially says “if this didn’t error, what the function asserts is true and can be understood for the rest of your method”.

Since we assert that input is a number, in this case, the functions available to numbers become available and so TypeScript has no problems with our toFixed call.

So, here’s where I am on this one: if your methods are long enough that you need assertion functions, you should probably split those up into smaller methods.

You could argue that assertion functions are a way of getting TypeScript to do some runtime type checking instead of the standard static checking it does at compile time only.

However, I don’t think that TypeScript thrives by enforcing rules at runtime. In my opinion, we should enforce our typings at compilation and then validate external input to the system at the edges. Things like API calls and user input should be asserted and cast, not your main application code.

Still, assertion functions are something to consider and watch as they potentially serve other uses down the road.

The Declare Keyword

Declare lets us combine the dynamic typing system with inheritance to essentially re-declare inherited properties.

Take a look at the following simple hierarchy:

Here we have a DarkTheme that inherits from Theme. Theme declares a collection of Person entities, which is itself an abstraction.

Because we know all people who use dark themes are awesome, we know that the users property will also only have AwesomePerson entities.

With TypeScript 3.7, TypeScript can understand this too.

We use the declare keyword to tell TypeScript to make assumptions about something without emitting anything particular for this assumption. Previously I’ve used declare to reference things like external libraries loaded on shared web pages.

Here we’re using declare to specify that a property has different typings in that context than previously defined.

I really like this feature. While not as commonly used as other language features, this helps team with complex hierarchies to understand their properties and not need to make type assertions.

Uncalled Function Checks

Finally, TypeScript now will catch a common error we frequently make with functions. Take a look at the following code:

Here we meant to invoke person.onlyDoesBoringThings at line 10, but forgot the ()‘s and are instead evaluating the function against null / undefined. The function is defined, so that condition evaluates as true even though invoking it would have returned fasle.

TypeScript 3.7 catches this error out of the box:

This condition will always return true since the function is always defined. Did you mean to call it instead?

This simple built-in check should improve your quality with no extra steps needed and so I’m all for it.

Next Steps with TypeScript 3.7

If you’d like to learn more about these features or other improvements to TypeScript, take a look at the full release notes.

You can update to TypeScript 3.7 via npm by running npm update -g typescript.

If you haven’t gotten started yet with TypeScript, check out my article on migrating existing JavaScript code to TypeScript.

What do you think of these changes? What are you most excited about? Do you have a compelling reason to use assertion functions I’ve not thought of?

The post How TypeScript 3.7 Helps Quality appeared first on Kill All Defects.

Top comments (14)

Collapse
 
ap13p profile image
Afief S

Does "Uncalled function check" has wrong screenshot? Because the screenshot show "declare" error instead of uncalled function check

Collapse
 
integerman profile image
Matt Eland

Yeah... I think I cropped it to the wrong error. It should have read: This condition will always return true since the function is always defined. Did you mean to call it instead?

I've made the edit.

I feel shame. Don't publish tired.

Collapse
 
kpulkit29 profile image
Pulkit Kashyap

I think some of the examples are not in the right order. Nice otherwise :)

Collapse
 
integerman profile image
Matt Eland

As in you'd like to see the functions reordered within the gists for readability?

Collapse
 
kpulkit29 profile image
Pulkit Kashyap

Ya kind of

Collapse
 
psalaun profile image
Pierre Salaün • Edited

The gists don’t seem to be in the right order, as some of them don’t match the text surrounding them.

Collapse
 
boredcity profile image
boredcity

example for Nullish Coalescing doesn't really seem to explain why it's useful: "old way" would just be var calculator = someCalculator || new Calculator();

Collapse
 
davinc profile image
Danijel Vincijanović • Edited

I was also wondering what's the difference between those two and I've found it. Nullish Coalescing better handles cases when it comes to truthy/falsy values.

For example:

const a = 0
const b = a || 100

console.log(b) // 100

Same thing, but different result with Nullish Coalescing:

const a = 0
const b = a ?? 100

console.log(b) // 0
Collapse
 
integerman profile image
Matt Eland

I personally hate the use of the || operator in this context, but that's mostly because my background is a C# one where I read the operator and think that b would be assigned to true since a and 100 would both be evaluated as booleans.

Obviously they wouldn't, but to me ?? is way more intuitive than the use of || in this context.

Collapse
 
boredcity profile image
boredcity

I thought I remembered something like this being the edge case where this was useful... Thank you, this seems like a better example!

Collapse
 
n4bb12 profile image
Abraham Schilling

Yessss I've been waiting for Optional Chaining and Nullish Coalescing so hard, thanks for writing this up and appearing on my newsfeed 🙌

Collapse
 
integerman profile image
Matt Eland

I know. Almost every merge request I review in the JavaScript / TypeScript world I wish the committer had access to optional chaining.

Collapse
 
bnaya profile image
Bnaya Peretz

The decalre keyword is not really a feature, but compatebility option with es class members
devblogs.microsoft.com/typescript/...

Collapse
 
macsikora profile image
Pragmatic Maciej • Edited

Some more details about why optional chaining is not so perfect thing
dev.to/macsikora/what-is-wrong-wit...