Type assertion with as any tells TypeScript to trust you:
const value: unknown = getUserInput();
const number = (value as any).toFixed(2); // No type checking
TypeScript won't verify that value has a toFixed method. If it doesn't, runtime error.
What 'as any' Does
as any casts a value to the any type, which opts out of type checking:
const obj: any = { name: 'Alice' };
obj.nonExistentMethod(); // No error - runtime crash
obj.foo.bar.baz; // No error - runtime crash
TypeScript accepts anything on any. This defeats the purpose of TypeScript.
When It's Justifiable
Working with untyped third-party libraries:
import oldLibrary from 'old-untyped-library';
// Library has no types, doesn't work as expected
const result = (oldLibrary as any).obscureMethod();
If the library has no @types package and you need it immediately, as any unblocks you. But add a TODO to properly type it later.
Migrating JavaScript to TypeScript:
// Legacy JS code being gradually migrated
function processLegacyData(data: any) {
// Temporarily any during migration
return data.transform();
}
During large migrations, any lets you incrementally add types without breaking everything.
Prototyping and debugging:
// Quick prototype - will type properly later
const temp = response as any;
console.log(temp.someProperty);
Fine for throwaway code. Not fine in production.
When It's Wrong
Avoiding type errors you should fix:
// Bad - hiding a real problem
function process(data: string) {
return (data as any).toUpperCase();
}
data is already string. No cast needed. If TypeScript complains, there's a real issue.
Because you don't know the type:
// Bad
function handleEvent(event: any) {
console.log(event.target.value);
}
// Good - use proper event type
function handleEvent(event: React.ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
Laziness:
// Bad - too lazy to type API response
const data = (await response.json()) as any;
// Good - define the type
interface User {
id: number;
name: string;
}
const data: User = await response.json();
Better Alternatives
Use 'unknown' instead:
// With any - no safety
const value: any = getInput();
value.toUpperCase(); // No error even if value is number
// With unknown - must check first
const value: unknown = getInput();
if (typeof value === 'string') {
value.toUpperCase(); // Safe - type narrowed
}
unknown requires type checking before use. It's safer than any.
Define proper types:
// Bad
const config: any = JSON.parse(file);
// Good
interface Config {
apiUrl: string;
timeout: number;
}
const config: Config = JSON.parse(file);
Use type guards:
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
if (isUser(data)) {
console.log(data.name); // TypeScript knows data is User
}
Use specific assertions:
// Bad - overly broad
const element = document.querySelector('.button') as any;
element.click();
// Good - specific type
const element = document.querySelector('.button') as HTMLButtonElement;
element.click();
Migration Strategy
If inheriting a codebase with any everywhere:
- Enable
noImplicitAnyin tsconfig.json - Fix implicit
anyoccurrences - Search for explicit
anytypes - Replace with proper types, starting with function boundaries
- Use
unknownas a safer temporary alternative
Tracking 'any' Usage
ESLint rule to flag any:
// .eslintrc.js
{
"@typescript-eslint/no-explicit-any": "error"
}
This warns on every any. For migrations, use "warn" instead.
Documenting Necessary 'any'
When any is justified, explain why:
// Third-party library with no types available
// TODO: Create type definitions or find @types package
const sdk = (window as any).ThirdPartySDK;
Double Assertion
Sometimes you need two assertions:
const value = (someValue as unknown) as TargetType;
TypeScript might reject direct as TargetType if types are incompatible. Going through unknown first works, but it's a code smell—you're forcing a type that TypeScript doesn't believe is safe.
Generic Constraints
Instead of any, use bounded generics:
// Bad
function getValue(obj: any, key: string) {
return obj[key];
}
// Better
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
Type Predicates
For runtime checks, use type predicates instead of any:
function processValue(value: unknown) {
if (typeof value === 'string') {
return value.toUpperCase();
}
if (typeof value === 'number') {
return value.toFixed(2);
}
throw new Error('Unsupported type');
}
The Cost of 'any'
any propagates through your code:
function process(data: any) {
return data.map(item => item.value);
// Return type is any[] - lost all type info
}
const result = process(input);
result.forEach(item => {
item.whatever(); // No error, but might crash
});
One any infects the entire call chain.
Strict Mode Configuration
Enable strict mode to catch any issues:
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
Code Review Guidelines
When reviewing as any usage, ask:
- Can this be typed properly?
- Can we use
unknowninstead? - Is there a type guard we could write?
- Is this temporary code or permanent?
- Is there a comment explaining why?
Further Reading
The TypeScript handbook's section on any vs unknown explains the difference.
Matt Pocock's TypeScript tips cover better alternatives to any.
Basarat's TypeScript Deep Dive explores the type system in depth.
as any is an escape hatch that should rarely be opened.
0 comments