Using any in TypeScript gives us a false sense of safety
TypeScript
is a strict syntactical superset of JavaScript
that adds optional static typing to it. In a few words, it makes JavaScript
hell stronger and safer language.
The web needs TypeScript
since we tend to build bigger and more complex applications with JavaScript
but the language itself has some well-known weaknesses. This is where TypeScript
comes into the picture to fill some blanks.
# Why TypeScript?
Let's see a dead simple example then:
// With plain JavaScript
const sum = (a, b) => a + b;
// With TypeScript
const sum = (a: number, b: number) => a + b;
const c = sum(2, 3);
// c === 5
So we created an arrow function named sum
and we declared its arguments as numbers. That way we are certain that the outcome will always be a number which is what we want actually.
Let's say we accidentally pass a string
that looks like number to the eye, but it definitely isn't an actual number for JavaScript
. What will be the outcome for each case?
// With plain JavaScript
const c = sum('2', 3);
// c === 23
// With TypeScript
const c = sum('2', 3);
// Argument of type '"2"' is not assignable to parameter of type 'number'.ts(2345)
As we saw JavaScript
kept on working by simply concatenating the two arguments while TypeScript
raised an error by warning us that we passed a faulty argument that is not a number
.
This is extremely beneficial since we are in position to spot such an error during development and tackle it early enough. The worse thing is that JavaScript
kept on working even with a faulty argument, so we could easily let this slip through in our application if we don't pay attention or even worse if we haven't established a robust suite of tests.
# Why any?
There are times though that we get lazy or we are not in position to declare a type accurately. Don't get mistaken, the example above was trivial. The more complex applications we are building the more challenging it becomes sometimes to declare accurately an interface or a type.
This is where any
comes into the game π
If you have used TypeScript
I am sure you can recall a bunch of times during the early days that you wanted even to cry. It happens, I know and you shouldn't let it get into your head. You can win this battle πͺ
Let's refactor the example above by applying any
type:
const sum = (a: any, b: any) => a + b;
We replaced number
type with any
so we can now accept any possible type in there.
That said, if we pass the same pair of arguments we did before, these are the results:
const c = sum(2, 3);
// c === 5
const c = sum('2', 3);
// c === 23
Arghhh, so we did pass a string
in the second example and we ended up with a bloody concatenation same as we did before with plain JavaScript
. Ok, but it is TypeScript
, right? We are safer and we will fix it in the future. No?
No you won't fix it and this is definitely not safe. There is no point in using TypeScript
if you force it to behave like plain JavaScript
.
Sorry, this is bad and will harm your project sooner or later π€·π»ββ
# Can we protect ourselves?
The truth is that we might need to use any
especially while migrating a JavaScript
application to TypeScript
. For all other cases we need to avoid using any
type in order to enjoy type safety.
TypeScript
has predicted this by providing a flag variable we can use in its tsconfig.json
configuration file named noImplicitAny
. So if this is equal to true
then TypeScript
will start screaming every time it spots any
type.
This configuration option can prevent us from doing silly mistakes by forgetting having any
types here and there .
{
...
"noImplicitAny": true,
...
}
# Can we still override this?
We can still force TypeScript
to ignore an error even with noImplicitAny
enabled, by using ts-ignore
. This will suppress all errors for the very next line only.
Below we haven't declared a type for user
argument, so we will get a warning:
const greeting = user => {
const { name } = user;
return `Hello ${name}`;
};
// Parameter 'user' implicitly has an 'any' type.ts(7006)
We can suppress this warning with ts-ignore
:
// @ts-ignore
const greeting = user => {
const { name } = user;
return `Hello ${name}`;
};
Obviously this is a bad practice, so we need to use it sparingly.
In the official docs we are advised against this too:
Please note that this comment only suppresses the error reporting, and we recommend you use this comments very sparingly
# Is there an alternative?
If we really don't know what is the actual interface we need to declare - let's say for an API response - we can still avoid using any
by taking advantage of unknown
type.
This was introduced in TypeScript 3 and as it is declared in the official docs:
unknown
is the type-safe counterpart ofany
. Anything is assignable to unknown, but unknown isnβt assignable to anything but itself and any without a type assertion or a control flow based narrowing. Likewise, no operations are permitted on an unknown without first asserting or narrowing to a more specific type
This means that we can use unknown
and run some logic, but we cannot do unsafe operations.
This will work:
const greeting = (name: unknown) => `Hello ${name}`;
greeting(1);
// Hello 1
greeting('John Doe');
// Hello John Doe
But the following will throw an error since we don't actually know more details about user
argument so it isn't safe to destructure it:
const greeting = (user: unknown) => {
const { name } = user;
return `Hello ${name}`;
}
// Property 'name' does not exist on type 'unknown'. ts(2339)
That way we manage to stay type-safe even when we are in a tricky situation, and we are tempted to use any
. Cheers!!
You can read even more regarding unknown
type in the official docs here
Did you like this one?
I hope you really did...
Newsletter
Get notified about latest posts and updates once a week!!
You liked it?