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
| Scenario | Use this |
|---|---|
| Unknown external data | unknown |
| Union of known types | string | number | boolean |
| Flexible object shape | Record<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.