Use `unknown` instead of `any` for values with uncertain types. Unlike `any`, `unknown` forces you to narrow the type before using it, keeping type safety intact.
`unknown` is the type-safe counterpart to `any`. Both accept any value, but `unknown` requires you to narrow (check) the type before accessing properties or calling methods. This means bugs are caught at compile time instead of crashing in production. Switching from `any` to `unknown` typically costs 1-2 lines of narrowing code but prevents entire categories of runtime errors.
BeforeMerge scans your pull requests against this rule and 3+ others. Get actionable feedback before code ships.
When you receive a value from an external source — an API response, a JSON parse, a catch block, a message event — you genuinely don't know its type. Both any and unknown can represent this uncertainty, but they behave very differently:
any: TypeScript says "I'll trust whatever you do with this value." No checks, no errors, no safety.unknown: TypeScript says "You must prove what this value is before you use it." You get compile-time errors until you add type guards.The difference is whether TypeScript protects you or abandons you. With unknown, you write a few extra lines of narrowing code. With any, you discover the bug in production.
Use unknown instead of any whenever a value's type is genuinely uncertain. Then narrow the type with typeof, instanceof, type guards, or Zod/schema validation before using it.
// BAD: any lets you access anything without checking
async function fetchUser(id: string): Promise<any> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
const user = await fetchUser("123");
console.log(user.naem); // typo not caught — crashes silently// GOOD: unknown forces you to validate
interface User {
id: string;
name: string;
email: string;
}
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value &&
"email" in value
);
}
async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const data: unknown = await response.json();
if (!isUser(data)) {
throw new Error("Invalid user response");
}
return data; // fully typed as User
}Search for any annotations that could be replaced with unknown:
grep -n ": any" --include="*.ts" --include="*.tsx" -r src/Focus on function parameters, return types, and variable declarations that represent external data.
: any with : unknown on the variable/parameter