When considering function definitions, I believe that the strict ban on using any
, often expressed as: "never use it except temporarily for transitioning from JavaScript to TypeScript," is overly simplistic. In particular, I find that the restriction against any
isn't as absolute when it serves as a constraint on a generic type parameter within a function. In this context, I believe any
can be quite beneficial.
I agree that any
is problematic in the example you provided.
getTranslationByKeys(keys: any): any {}
This completely disregards the type system. For instance, consider this call site:
const myResult = getTranslationByKeys(keys: [1,3,4])
I have no clue if this function can process the parameters I've supplied or what the result will be once it's finished (if it even finishes):
Clearly not ideal. However, let's examine the following function typing:
function getTranslationByKeys<T extends any>(keys: T): T {}
It contains the term any
. Although numerous lint rules and educational materials on TypeScript may criticize its usage unless temporary for an update, I don't see it as problematic in this scenario. Why? Because unlike employing any
as an unadulterated type, here you're not completely bypassing the type system. Essentially, you still have robust type safety at the call site. For example, a call like:
const myResult = getTranslationByKeys(keys: [1,3,4])
would indeed be strongly typed. myResult
would possess the type
number[]</code, and (in my view), the absence of a constraint on <code>keys
indicates to the user that any type of parameter is acceptable.
Granted, some level of type safety inside the function definition is compromised, whereby keys
could potentially be anything. Sometimes, however, that's precisely what is needed. You might have a function capable of handling various input types, such as this (albeit nonsensical) function:
function getTranslationByKeys<T extends any>(keys: T): T {
console.log(keys)
return keys
}
While that specific code snippet might seem absurd, my point is that there are no issues with the typing of the code. It retains strong typing at the call site and accurately represents the function in the definition. Allowing the utilization of any
!
Some may argue that what I've articulated above mirrors simply writing getTranslationByKeys<T>
, which is commonly seen and technically doesn't include the term any
. However, this distinction doesn't alter the situation. The two statements are equivalent, and one shouldn't be favored solely because any
is concealed.
Furthermore, any
can prove highly beneficial within generic constraints. Take into account this example:
function getTranslationByKeys<T extends Record<string, any>>(keys: T): T {}
Here too we encounter the dreaded word any
, yet it functions as a valuable constraint. It specifies that T
can assume any object form—as long as it's objectively an object with string-based indices. This constraint is far more precise than merely stating T extends object
. I routinely employ and appreciate this application of any
within function definitions.
Could you substitute unknown
here instead of any
? Perhaps. Admittedly, I haven't employed unknown
extensively in conjunction with generic constraints to ascertain its equivalence in behavior. From my perspective, though, unknown
appears less indicative of your intentions in the aforementioned scenario. To the function's consumer, you're not saying "provide me with a string-indexed object with unknown value types"; rather, you essentially mean "you can supply a string-indexed object with any values"—precisely what any
embodies!
Hence, I contend that any
, utilized within the framework of constraints on generic type parameters, can offer significant value and clarity. Therefore, it should not be subject to the broad prohibition frequently associated with utilizing any
as a basic type.
I would love to hear others' perspectives on this matter.