JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

Follow publication

Introduction to Duck Typing in TypeScript

--

In this article, I discuss duck-typing as a practice and a cool feature of TypeScript that makes a huge difference — type predicates.

What is “Duck Typing”?

Duck typing is a common practice in many OOP languages. It got its name from the so-called “Duck Test”, that is:

If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck

Or as Monty Python would have it:

Monty Python and the Holy Grail (1975), Witch Trial Scene

Duck Typing is usually used in code that needs to handle a range of different data, often without knowing exactly what parameters will be passed by a caller. Think here of some of the uses you encountered for switch statements or complex if/else blocks. These are typically places where Duck Typing might come in handy or even offer an alternative.

Why Duck Type?

A common pattern for duck typing in dynamic languages is to try and perform an action assuming a given value fits what we expect, and then handle whatever exceptions might arise. Python is a good example here:

This is evidently a silly example, but the point is simple — you get a value, you check if it quacks by calling its .quack() method- if it quacks, return true, if either an attribute or value error is raised, return false.

In Python try-except is an accepted pattern, one that is also used internally by built-ins such as hasattr and throughout the standard library. In JavaScript in contrast, try-catch is more restricted — you can neither define different catch blocks depending on the prototypes of the errors that have been thrown, nor for that matter even be certain that what has been thrown is even an instance of error at all.

You, therefore, have to be more verbose and a lot safer when handling errors, which makes this somewhat of an anti-pattern in JavaScript and TypeScript. The common practice instead is to do something like this:

In the above predicate we (1) check that the parameter value is of type “object”, (2) that it is not null because the type of null is “object” in JavaScript (🤦‍♂), and (3) using the Reflect.get method, we retrieve the value for “quack” safely and check that it is indeed a function.

This kind of predicate is probably familiar to most readers — after all, JavaScript code is often filled with boolean checks, be they abstracted into separate functions or simply written inline.

Yet this is where JavaScript and TypeScript differ — the parameter value might be a duck, but neither the IDE nor the JavaScript interpreter know what a duck is. In TypeScript on the other hand, Duck can and will be a type:

Notice the is keyword used in the return value typing of isDuck, this is what’s called a type predicate in TypeScript, and it’s one of the nicer features of the language: A type predicate is a function returning a boolean value that acts as a custom type guard; in effect telling the TypeScript compiler that a given value is of a given type. That is, in the above example, if the function isDuck returns true, the compiler will know that the value has the type Duck.

Why is this a big deal? Because our function now has dual utility — it remains a predicate, returning a boolean value, which means we can use it like a predicate in terms of JavaScript, but at the same time, it also affects the TypeScript compiler and thereby the IDE and any other tooling (read ESLint) that might be linked to the compiler.

Example Use Case: recursiveResolve

One handy use for duck typing is when you have code that might accept both Promises and non-Promises. The built-in way to handle this is to use Promise.resolve() to wrap the value, this will either unpack the Promise object if given a Promise object— or wrap the value in a Promise object and then unpack it. The problem with this is that it has a slight overhead — you will need to await resolution even for non-promise values.

An alternative approach would be to “duck type” promises using a type predicate, which by convention would be called isPromise.

Let’s assume we created a custom method to recursively traverse an object, resolving whatever promises might be nested inside it (the code below is adapted from one of my libraries, you can see the original here), which is a good use case for such a type predicate:

As you can see we defined two predicates in the above — isPromise and isRecord, both of which accept an optional generic parameter, which makes them reusable. We are then able to use them inside the recursiveResolve function with very little overhead with typing being correctly inferred throughout the function.

Bonus: Predicate Libraries

Given the common use case here, you can find type predicates in many different libraries. If you work in Node.js, you can use ready-made type predicates, which ship as part of the node/util package. There is also a browser port of these available. I would though recommend another library, one of my own making, called @tool-belt/type-predicates which offers a more comprehensive and significantly better typed collection of type predicates and type assertions. Either way, you can do without any libraries as well — depending on your use case—and simply write predicates as you require them.

More content at plainenglish.io. Sign up for our free weekly newsletter here.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Responses (3)

Write a response