Fundamentals 8 min read

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.

Code Mala Tang
Code Mala Tang
Code Mala Tang
When Is Using ‘any’ in TypeScript Actually Acceptable?

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

type safetyConditional TypesGeneric Typesany type
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.