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)
Does "Uncalled function check" has wrong screenshot? Because the screenshot show "declare" error instead of uncalled function check
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.
I think some of the examples are not in the right order. Nice otherwise :)
As in you'd like to see the functions reordered within the gists for readability?
Ya kind of
The gists don’t seem to be in the right order, as some of them don’t match the text surrounding them.
example for Nullish Coalescing doesn't really seem to explain why it's useful: "old way" would just be
var calculator = someCalculator || new Calculator();
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:
Same thing, but different result with Nullish Coalescing:
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 thatb
would be assigned totrue
sincea
and100
would both be evaluated as booleans.Obviously they wouldn't, but to me
??
is way more intuitive than the use of||
in this context.I thought I remembered something like this being the edge case where this was useful... Thank you, this seems like a better example!
Yessss I've been waiting for Optional Chaining and Nullish Coalescing so hard, thanks for writing this up and appearing on my newsfeed 🙌
I know. Almost every merge request I review in the JavaScript / TypeScript world I wish the committer had access to optional chaining.
The decalre keyword is not really a feature, but compatebility option with es class members
devblogs.microsoft.com/typescript/...
Some more details about why optional chaining is not so perfect thing
dev.to/macsikora/what-is-wrong-wit...