Reader small image

You're reading from  TypeScript 4 Design Patterns and Best Practices

Product typeBook
Published inSep 2021
Reading LevelIntermediate
PublisherPackt
ISBN-139781800563421
Edition1st Edition
Right arrow
Author (1)
 Theofanis Despoudis
Theofanis Despoudis
author image
Theofanis Despoudis

Theo Despoudis lives in Ireland, where he works as a Software Engineer for WP Engine and as a part-time tech practitioner for Fixate. He is the co-author of The React Workshop and Advanced Go Programming in 7 Days, Dzone Core Member, and maintains some open source projects on GitHub. Theo is available for conference talks, independent consulting, and corporate training services opportunities.
Read more about Theofanis Despoudis

Right arrow

Chapter 9: Anti-Patterns and Workarounds

Just as there are good design patterns and best practices while using TypeScript, there are some anti-patterns as well. When working on large-scale applications, you will inevitably come across some patterns or parts that look problematic, are hard to read and change, or promote dangerous behavior. This happens because as the application grows, you will need to write more code that fits the existing code base and quite often you will have to make some compromises. Over time and as more people are contributing to the same code space, you will see many inconsistencies. In this chapter, we look at approaches to work around these problems.

In this chapter, we will gain an understanding of what happens when you overuse classes and inheritance in a system and how to avoid it. Then we will explain the dangers of not using runtime assertions in code. Afterward, we will show how permissive or incorrect types essentially ignore the type safety of your...

Technical requirements

The code bundle for this chapter is available on GitHub here:  

https://github.com/PacktPublishing/TypeScript-4-Design-Patterns-and-Best-Practices/tree/main/chapters/chapter-9_Anti_patterns

Class overuse

OOP principles and design patterns promote a type of programming where you try to model the real world using classes and entities. While the benefits of OOP are well recognized and understood, quite often OOP can lead to a proliferation of classes.

To explain what we mean, when you try emulating a system using classical OOP techniques such as inheritance and encapsulation, you inevitably have to carry over the whole hierarchy.

This leads to the banana, monkey, jungle problem. You want to use a banana object but in order to get the banana object you will have to import the Jungle object that holds the monkey instance that exposes the getBanana() method:

new Jungle().getAnimalByType("Monkey").getBanana();

This example code indicates that the problem lies with how you structure your classes and how you utilize them in the code.

Practically this means that when OOP techniques are heavily utilized, the benefits of reusability and inheritance fade...

Not using runtime assertions

TypeScript safety comes from compile-time checks when writing code. You define a type for a value and then TypeScript verifies its fair usage. This process, however, covers only 50% of the safety in general. The other 50% comes from runtime safety. For example, let's take the following function in TypeScript:

RuntimeAssertions.ts

function divMod(x: number, y: number): [number, number] {
  return [Math.floor(x / y), x % y];
}

This gets compiled in JavaScript as follows:

"use strict";
function divMod(x, y) {
    return [Math.floor(x / y), x % y];
}

Looking at the aforementioned code, you could guess that there are many potential dangers here:

  • The x and y parameters might not be numbers. If they are numbers encoded as strings, it will work but not if they are not a real number value:
    > divMod("1", 2)
    (2) [0, 1]
    > divMod("a", 2)
  • Here in the second case, we passed...

Permissive or incorrect types

Reasonably often, you will be asked to integrate a library that does not include TypeScript declarations or develop a new feature from scratch. Initially, you will be tempted to do integrate such libraries when defining objects and classes and try to make things work, avoiding spending more time on precisely defining the types they use. This could lead to cases where TypeScript will not type check your code correctly or apply the wrong checks.

Here, we explain the most obvious uses of permissive or incorrect types:

  • any: The any type is equivalent to opting out of type checking. This means the compiler will not check the type of the variable or parameter and it will pass it on as it is. Using any as a type is not recommended as you'll lose all security and features of the type system.
  • Function: Function is a generic container type that represents any function type. This is an example of a permissive type, as Function can be any kind...

Using idiomatic code from other languages

Sometimes, when coming from a different programming language background, it's tempting to use patterns and idioms that are prevalent there as a result of these language limitations. Although many programming concepts are shared between many languages, such as control loops, classes, functions, and so on, there are many other concepts particular to one language that cannot be used in a different language.

In the next subsections, we show some obvious cases where using some idiomatic constructs from other languages will not work well with TypeScript, starting first with the Java language.

From the Java language

If you are coming from a Java background, then you work mainly with classes; so OOP principles are prevalent here. One common pattern in the Java world is the use of Plain Old Java Objects or POJOs for short.

This is just a naming convention for creating a class that follows some rules, especially in the context of Java...

Type inference gotchas

When working with type inference in TypeScript, sometimes you need to be explicit in regard to types, but most of the time, implicit typing is preferred. There are some caveats that you need to be aware of. We'll explain when it makes sense to use inference.

In TypeScript, you can either declare types for variables or instances explicitly or implicitly. Here is an example of explicit typing:

const arr: number[] = [1,2,3]

On the other hand, implicit typing is when you don't declare the type of the variable and let the compiler infer it:

const arr = [1,2,3]// type of arr inferred as number[]

If you declare and do not assign any value within the same line, then TypeScript will infer it as any:

let x; // fails with noImplicitAny flag enabled
x = 2;

This will fail to compile with the noImplicitAny flag, so in that case, it's recommended to always declare the expected type of the variable.

Another case is when you declare a function...

Summary

In this concluding chapter, we explored some of the most important caveats and anti-patterns when working with TypeScript. In general terms, TypeScript is very adaptive as a language and accepts alternative programming models. However, you should strive to avoid confusion when developing with types, prefer plain functions and types over classes, and leverage type inference when needed.

This concludes our book TypeScript 4 Design Patterns and Best Practices. Throughout this book, we explored the practical details of creational, structural, and behavioral design patterns, studied functional and reactive programming concepts, and discussed some best practices for TypeScript development.

Now it's up to you how you utilize the content of this book in your everyday work. For example, while designing new applications or when discussing TypeScript best practices with your colleagues, you are now armed with a deep understanding of the benefits and caveats of each pattern...

Q & A

  1. What is a runtime assertion?

    When a runtime assertion fails, then the whole program should fail and notify the developers of that error case. This is because assertions represent irrecoverable errors and we should be able to find out the source of the problem early. By using them in places where you expect a value or a type to exist at runtime, you uncover bugs.

  2. How do you understand black-box reuse in the context of object composition?

    Black-box reuse means that you use a component without knowing its internals. All you possess is a component interface. At that time, you test it without knowing or expecting a particular library or a function to trigger because this is concealed. With black-box reuse, you can debug and test code many times and in alternative scenarios, and it closely follows the Liskov substitution principle.

Further reading

Why subscribe?

  • Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals
  • Improve your learning with Skill Plans built especially for you
  • Get a free eBook or video every month
  • Fully searchable for easy access to vital information
  • Copy and paste, print, and bookmark content

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at packt.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at customercare@packtpub.com for more details.

At www.packt.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
TypeScript 4 Design Patterns and Best Practices
Published in: Sep 2021Publisher: PacktISBN-13: 9781800563421
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Theofanis Despoudis

Theo Despoudis lives in Ireland, where he works as a Software Engineer for WP Engine and as a part-time tech practitioner for Fixate. He is the co-author of The React Workshop and Advanced Go Programming in 7 Days, Dzone Core Member, and maintains some open source projects on GitHub. Theo is available for conference talks, independent consulting, and corporate training services opportunities.
Read more about Theofanis Despoudis