Avoid any
The any type is an escape hatch that turns TypeScript back into JavaScript for the value it touches. Every property access, method call, and assignment on an any is permitted with zero checking, and worse, any spreads outward: anything derived from it also becomes any. A single careless any can silently disable type checking across an entire call chain. This page explains the real costs of any, what to reach for instead, and how to enforce its absence with the compiler and ESLint.
What any actually costs
any disables type checking in both directions — you can assign anything to it, and assign it to anything. That second direction is what makes it dangerous.
function parse(raw: any) {
return raw.user.name.toUpperCase(); // no error, even if any of these is undefined
}
const id: number = parse("hello"); // ❌ no error — 'any' assigns to 'number'
Nothing here is checked. If raw.user is undefined, you get a runtime crash with no compile-time warning. And because parse returns any, the bogus assignment to id is accepted too. The bug surfaces in production instead of your editor.
any vs unknown vs the right type
When you genuinely don’t know a type yet, you have better options than any. The table shows the trade-offs.
| Type | Assignable from anything | Usable without narrowing | Type-safe |
|---|---|---|---|
any | Yes | Yes | No — checks disabled |
unknown | Yes | No — must narrow first | Yes |
| a precise type | Only matching values | Yes | Yes |
unknown accepts any value just like any, but forces you to prove what it is before using it. A precise type is best of all. Reaching for any should feel like a last resort, not a default.
Reach for unknown at boundaries
Untyped data enters your program at boundaries — JSON parsing, third-party libraries, network responses. Type those entry points as unknown and validate before use.
function readConfig(json: string): { port: number } {
const data: unknown = JSON.parse(json);
if (
typeof data === "object" && data !== null &&
"port" in data && typeof data.port === "number"
) {
return { port: data.port };
}
throw new Error("Invalid config");
}
The same shape with any would compile happily and crash later. With unknown, the compiler refuses to let you touch data.port until you’ve checked it, so the validation is mandatory rather than optional.
Use generics instead of any
People often reach for any when they really want a generic — a function that works over many types while preserving them.
// ❌ loses all type information
function firstAny(arr: any[]): any {
return arr[0];
}
// ✅ preserves the element type
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
const n = first([1, 2, 3]); // n: number | undefined
The generic version keeps the relationship between input and output, so callers get real types back. any throws that relationship away.
The two safest stand-ins for
anyareunknown(when a value’s type is genuinely not known) and a type parameter<T>(when you want to preserve whatever type the caller supplies). Reach for these beforeanyalmost every time.
Enforce it with tooling
Don’t rely on discipline alone — make the compiler and linter forbid any. noImplicitAny (part of strict) catches implicit anys; an ESLint rule catches the explicit ones you write by hand.
// tsconfig.json
{ "compilerOptions": { "noImplicitAny": true } }
// eslint.config.js (typescript-eslint)
{
"rules": {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-member-access": "error"
}
}
noImplicitAny stops untyped parameters and variables from silently becoming any. no-explicit-any flags the keyword itself. The no-unsafe-* rules catch anys that leak in from untyped libraries even when you never wrote any yourself.
Best Practices
- Treat
anyas a code smell — every use should be justified by a comment. - Prefer
unknownfor values whose type you don’t yet know, then narrow. - Use generics when you want to preserve a caller’s type instead of erasing it.
- Enable
noImplicitAny(viastrict) and the ESLintno-explicit-anyrule. - Add the
no-unsafe-*typescript-eslint rules to catch anys leaking from dependencies. - When you must use
any, isolate it behind a typed function so it can’t spread. - Replace
any[]andFunctionwith precise array element and signature types.