Marcello Bragadin
← Coding Insights
TypeScript Python type safety best practices

Why You Should Never Use `any` in TypeScript (and Python)

The `any` type feels like a quick escape hatch, but it silently undermines everything static typing is meant to protect. Here's why to avoid it — and what to use instead.


Static typing exists to catch bugs at compile time, improve editor tooling, and make codebases easier to reason about. Using any in TypeScript — or its equivalent Any in Python — opts you out of all of that. Let’s look at why it matters, and what better alternatives exist.

The Problem with any in TypeScript

When you annotate something as any, the TypeScript compiler stops checking it entirely.

function processUser(user: any) {
  console.log(user.naem); // typo — but no error!
}

The typo naem would have been caught instantly if user had a proper type. With any, it silently becomes undefined at runtime.

The silent spread

The real danger is that any is contagious. Once a value is typed as any, every property you read from it is also any, every function return is any, and the unsafety propagates through your entire call chain.

const config: any = getConfig();
const timeout = config.server.timeout; // any
const ms = timeout * 1000;             // any — no arithmetic safety

What to use instead

ScenarioUse this
Unknown external dataunknown
Union of known typesstring | number | boolean
Flexible object shapeRecord<string, unknown>
Generic functions<T>(arg: T): T

unknown is the type-safe counterpart to any. It forces you to narrow the type before using the value:

function processResponse(data: unknown) {
  if (typeof data === "string") {
    console.log(data.toUpperCase());
     // safe — TypeScript knows it's a string
  }
}

ESLint to the rescue

Add the @typescript-eslint/no-explicit-any rule to your ESLint config to catch any usage before it merges:

{
  "rules": {
    "@typescript-eslint/no-explicit-any": "error"
  }
}

The Problem with Any in Python

Python’s typing module provides Any as a deliberate escape hatch for gradual typing. Like its TypeScript counterpart, it silences all type checkers (mypy, pyright, etc.).

from typing import Any

def get_value(data: Any) -> Any:
    return data["key"]  # No error even if data is an int

Mypy will happily ignore every operation on an Any-typed value, no matter how obviously wrong.

What to use instead

For unknown data at the boundary (e.g. JSON from an API), use object or the more expressive TypedDict:

from typing import TypedDict

class UserPayload(TypedDict):
    id: int
    name: str
    email: str

def process(user: UserPayload) -> str:
    return user["name"].upper()  # fully type-checked

For truly flexible inputs, use Union or the | syntax (Python 3.10+):

def stringify(value: str | int | float) -> str:
    return str(value)

For generic functions, use TypeVar:

from typing import TypeVar

T = TypeVar("T")

def first(items: list[T]) -> T:
    return items[0]

Enable strict mode in mypy

In your mypy.ini or pyproject.toml:

[tool.mypy]
strict = true
disallow_any_explicit = true

The Rule of Thumb

If you’re reaching for any, you’re usually solving the wrong problem.

Either the types are genuinely unknown — in which case unknown / object is the right boundary type — or you need generics to express a relationship between input and output types.

The few legitimate uses of any are narrow: auto-generated legacy code, interop with untyped third-party packages, or intentional escape hatches that are immediately narrowed with a runtime check.

Everywhere else, any is deferred technical debt. The type checker cannot help you where it’s been told to look away.