If you are doing extensive typescript development and have a codebase that is mix of typescript and javascript, I am sure you’ll be using the any
type quite often. Mostly to reference javascript objects/functions in typescript.
While using any
is about convenience, I consider it as an anti-pattern, and this post all about why and what can be done to avoid the any
trap.
The any
anti-pattern
Let’s understand what does typescript documentation say about any
.
The
any
type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type-checking during compilation.
The except seems to be validating the fact that any
enables us to use javascript code in typescript, albeit without the type safety net in place. Then why is it an anti-pattern?
The reason is quite simple!
With any
we lose the type safety and hence the benefits that are integral to any type safe language.
I’ll even argue that if you are using too much of any
, you are writing javascript in typescript. Instead, with a little bit of effort we can reduce any
usage to the bare minimum.
How do we avoid any
?
By simply creating interface definitions for any javascript code we use.
Defining type definitions
We all are familiar with the approach of including type definitions (or typings) for third party javascript libraries that we use with typescript. Anytime we use popular libraries such as jquery, angular, react in typescript we include the type definitions for these libraries as well.
We need to do something similar with our own javascript code that we reference in typescript. We need to create typings. Let’s take a quick example. Assuming you have a javascript UserService
class defined with few function and attributes.
function UserService() {
this.endPoint = '/api/users/';
this.getUsers = function() { ... }
this.getUser = function(userId) { ... }
this.createUser = function(user) { ... }
}
A type definition for the same UserService
in typescript would be:
export interface UserService {
endpoint: string;
getUsers();
getUser(userId: string);
createUser(user: User);
}
export interface User {
id: string;
name: string;
}
Now instead of passing UserService
as userservice: any
in typescript we can use userservice: UserService
and get all the intellisense and type safety!
While defining type/interface for every javascript piece that we reference may seem be a daunting task, in the long run it leads to a more maintainable code. Also remember, we do not have to create the complete type definition for the javascript class we are using, and can limit ourselves to defining types for properties/functions that we plan to use in typescript code. Type definition generation can be a gradual process!
Let’s make best use of this awesome language!