When Is Using ‘any’ in TypeScript Actually Acceptable?
This article examines the risks of the TypeScript any type, explains why it disables safety features, explores scenarios where any or unknown are appropriate, and demonstrates how to implement generic ReturnType utilities and conditional types with practical code examples.
In TypeScript, the any type is extremely powerful because it lets you treat a value exactly as you would in plain JavaScript, bypassing all TypeScript features such as type checking, autocomplete, and safety.
const myFunction = (input: any) => {
input.someMethod();
};
myFunction("abc"); // This will fail at runtime!Using any in real projects can cause several problems:
Loss of type safety: TypeScript no longer checks for potential errors, contradicting its purpose.
Harder maintenance: A codebase filled with any makes debugging and maintenance difficult because the type system cannot provide accurate information.
Hidden bugs: Since any allows calling non‑existent properties or methods, bugs may only surface at runtime.
In most cases any is considered bad code and linting tools like ESLint are used to prevent its usage, although there are situations where using any is the correct choice.
Type Parameter Constraints
Suppose we want to implement a ReturnType utility in TypeScript that accepts a function type and returns its return‑value type.
We need a generic type that takes a function type as its parameter. If we avoid using any, we might choose unknown instead, which is stricter and safer because it forces type checks before the value is used.
type ReturnType<T extends (...args: unknown[]) => unknown> = T extends (...args: unknown[]) => infer R ? R : never;ReturnType : defines a generic type that accepts a type parameter T .
T extends (...args: unknown[]) => unknown : constrains T to be a function whose parameters are unknown and whose return type is unknown .
T extends (...args: unknown[]) => infer R ? R : never : a conditional type that uses infer R to infer the function’s return type.
infer R : attempts to infer the return type R from a function matching the constraint.
: never : if inference fails (e.g., T is not a function), the result is never .
However, once we add a parameter, the unknown constraint fails, complaining that unknown cannot be assigned to string. Using any[] as the parameter constraint works for any function:
type ReturnType<T extends (...args: any[]) => any> =
T extends (...args: any[]) => infer R ? R : never;
const myFunction = (input: string) => {
console.log("Hey!");
};
type Result = ReturnType<typeof myFunction>;Now it behaves as expected, because we intentionally allow any argument types.
Returning Conditional Types from Generic Functions
Sometimes TypeScript’s type narrowing is insufficient. For example, you may want a function that returns different types based on a condition.
const youSayGoodbyeISayHello = (
input: "hello" | "goodbye"
) => {
if (input === "goodbye") {
return "hello";
} else {
return "goodbye";
}
};
const result = youSayGoodbyeISayHello("hello");Here result is inferred as "hello" | "goodbye", but the actual runtime logic should return "goodbye" when the input is "hello". To reflect the runtime behavior in the type system, we can add a conditional return type:
const youSayGoodbyeISayHello = <
TInput extends "hello" | "goodbye"
>(input: TInput): TInput extends "hello" ? "goodbye" : "hello" => {
if (input === "goodbye") {
return "hello" as TInput extends "hello" ? "goodbye" : "hello";
} else {
return "goodbye" as TInput extends "hello" ? "goodbye" : "hello";
}
};This approach requires writing each type explicitly. Using any makes the generic more flexible:
const youSayGoodbyeISayHello = <
TInput extends "hello" | "goodbye"
>(input: TInput): TInput extends "hello" ? "goodbye" : "hello" => {
if (input === "goodbye") {
return "hello" as any;
} else {
return "goodbye" as any;
}
};If we avoid any, TypeScript may fail to match the conditional type with the runtime logic, leading to type errors.
Conclusion
The lingering question is whether you should forbid any. The answer is generally yes: enable ESLint rules to prevent its use and avoid it whenever possible.
Nevertheless, there are cases where any is necessary; in those situations you can bypass the rule with eslint-disable.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
