---
title: 'TypeScript: Stop Using ''any'', There''s a Type For That'
teaser: 'Chances are you''ve worked with the `any` type in TypeScript. It''s widely
  overused, and subtly dangerous. Let''s explore why you should avoid it, and when
  you might truly need to use it.

  '
tags: typescript,types,good code
author:
- Alejandro Dustet
- Wil Hall
published_on: 2020-10-13
---

When we work with any(😬) amount of TypeScript code, chances are we will
encounter the `any` keyword. Most of the uses we have seen indicate that we are
dealing with the base type in TypeScript land. Ruby's is `Object` same for
C#, inside the [documentation] we find:

[documentation]: https://www.typescriptlang.org/docs/handbook/basic-types.html#any

> (...) values from code that has been written without
TypeScript or a 3rd party library. In these cases, we might want to opt-out of
type checking. To do so, we label these values with the any type:

## What is `any`

So it is not a `wildcard`, and it is not the base type; it is explicitly to
interact with 3rd party libraries. Then why is it around so often? Is it
detrimental to our systems? Should we run from it or embrace it?

> 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.

Let that sink in. The TypeScript documentation express
clearly that when we have the `any` type, we are telling the compiler:

![nothing to see here](https://images.thoughtbot.com/blog-vellum-image-uploads/qbevF7KQ96Kp5H78eYMu_giphy.gif)

We are saying `no thank you` when over 500 contributors to the language offer
their help. It sounds like to opt-out of the type checker, and with it, losing
all security and confidence in our type system should not be a decision taken
lightly. We should be using it to interact with non-typed 3rd party(or 1st
party) *Javascript* code, or when we only know some part of the type.

### But wait I have a lot of other reasons

#### Isn't TypeScript transpiling to Javascript? Isn't Javascript dynamic? Why should I take care of my types then?

Yes! But we are writing our code in TypeScript, which is a statically typed
language. One can argue that statically typed languages don't result in [fewer
bugs] than dynamic languages. Still, in a statically typed language where we use
something like `any`, that's the worst of both worlds.

[fewer bugs]: https://martinfowler.com/bliki/DynamicTyping.html

#### Some things are hard to type correctly, and `any` is easier

It is easy to indulge the lazy developer in us. If we don't type it correctly,
we're going to write bugs, more bugs than we would in a dynamic language because
we force TypeScript, a statically typed language, to go without checking for
incorrect types.

#### I really don't know what it is

That's fine! We can use `unknown`; it allows us to assign **any** type indeed.
But we won't be allowed to operate with the values until a specific
type is determined.

```typescript
type ParsedType = {
  id: number
}

const parseApiResponse(
  response: Record<string, unknown>
): ParsedType => {
  const convertedResponse = (response as ParsedType)

  // without doing the type cast we would
  // get a type error here
  if(convertedResponse.id >= 0) {
    return convertedResponse
  } else {
    throw Error.new("Invalid response"
  }
}
```

#### I have to write a lot of code when I add types, `any` is less work

It probably isn't; if we are writing code without types, we will likely add
defensive code to make sure arguments and variables have the correct shape for
the program to perform as intended. `any` can't even guard our logic against
`null` or `undefined` checks.

``` typescript
// version 1 with `any`
const fullName = (user: any) => {
  if (user?.firstName && user?.lastName) {
    return `${user.lastName}, ${user.firstName}`
  }

  return user?.firstName || ""
}

// version 1 without `any`

interface User {
  firstName: string
  lastName?: string
}

const fullName = ({ firstName, lastName }: User) => {
  if (lastName === undefined) {
    return firstName
  }

  return `${lastName}, ${firstName}`;
}
```

#### Types add so much complexity, sometimes `any` is simpler

Using `any` might allow us to make progress without putting too much thought
into how the data flows into our logic. But it shifts that burden to future
readers of our code. They will have to interpret what is happening without the
context we had and without help from the compiler.

#### With documentation I can provide all the context

When we add the types, we are getting help from the compiler, and we are getting
documentation that will not decay with time, because our code won't compile if
it is outdated.

``` typescript
const intersection = (a: any, b: any): any => {
...
}
const intersection = (
  a: Set<number>, b: Set<number>
): Set<number> => {
...
}
```

They are both equivalent, but the reader will have a better idea of what the
last function is doing, not so much from the first one.

#### But I've written the code in a defensive way with the necessary runtime checks to ensure there isn't an error

There might not be an error now, but unless you have excellent test coverage,
someone coming in to change that code later can't have confidence that they're
not refactoring in a bug; it's almost like the compiler won't help you because
we said it not to. If we explicitly set the types and change an `API` consumed
in our system, the compiler will offer its [guide].

[guide]: https://thoughtbot.com/blog/going-through-changes-with-typescript

#### What if I later change my mind about the type? Having it all explicit will have me refactoring for hours.

We can always modify and accommodate new type definitions. TypeScript offers
an array of [utilities] for this purpose. We can use `Pick` to, well, pick the
properties we need from a previously defined type. `Omit` to get everything but
a handful of them. `Partial` to make all attributes optional or do a full 180
and make them all `Required`.

[utilities]: https://www.typescriptlang.org/docs/handbook/utility-types.html

``` typescript
type User = {
  id: number;
  firstName: string;
  lastName: string;
  age: number;
}

type UserParams =
  Pick<User, "id"> & Partial<Omit<User, "id">>

const updateUser = (
  { id, ...newUserParams }: UserParams
) => {
  {...}
}
```

#### Fine, deleting any from TypeScript, opening a PR right now

Let's take a deep breath, `any` is extremely powerful and useful in the right
scenario.

- Interfacing with libraries that use it; make sure we turn it into the right
  type as soon as possible before moving that data through the system.

- Getting around TypeScript type bugs; if we find ourselves in a situation
  where it’s impossible to type something, `any` might be necessary. But only
  resort to this after trying every other approach. And if we use it, we should
  turn it back into a predictable type ASAP.

- If our function can genuinely handle any type, this is rare and situational
  (such as a debug or logging function maybe?) In these cases, we need to be
  100% sure there is **no type in existence** that will cause the function to
  fail. We should to examine the body of the function and determine the most
  basic shape from the input and restrict it. For example, if we want to print
  something, we should, at a minimum, verify that it responds to `toString`.
  Small steps.

### Let's recap:

Why shouldn't we use any again?

- It yields the compiler obsolete:
  * We're telling the compiler `Help not needed, I got this`
- We're passing on an opportunity to document the code as we write it
- Our first line of defense is down
- In a dynamic language, we assume that things can have any type, and the
  patterns we employ follow this assumption. If we start using a statically
  typed language as a dynamic one, then we're fighting the paradigm.
- As we continue to make changes to the codebase, there's nothing to guide/help
  us.
- With great freedom comes great responsibility (the compilers). Don't turn into
  a compiler, use one.
