I have a function defined like this:
export async function removeUser(deleteUserId: Types.ObjectId)
When I mistakenly called this function and pass a Mongoose object’s id
parameter, which is a string, it triggered a runtime exception further in the code when I tried to use the .equals()
method which exists on an ObjectId but not on a string:
await removeUser(deleteUser.id);
When I corrected this by passing the ObjectId, everything worked fine:
await removeUser(deleteUser._id);
My question is, why didn’t I get a compile time error from Typescript if the argument is specified as an ObjectId, but a string is being passed?
Edit: I see that the .id
is defined as any
, not as a string
. I suspect that will factor into the answer?
3
Answers
Yep, if we can somehow check for any, then we can make sure that values of type
any
cannot be passed:So if we pass in
any
, the type ofdeleteUserId
will turn intonever
:Playground
Values of type
any
allow assignment to any type.It’s an escape hatch from the type system. So
any
is not typesafe and should be avoided at all costs.See Documentation:
Typescript types exist only in compile time, not in runtime.
Therefore, Typescript type system is not sound — it cannot guarantee you that if your program compiles, it will not throw any error in runtime.
Back to your question, if a variable type is
any
, you can do anything with it, and Typescript will not even blink. You should useany
type with an extreme caution.For example, if you have a variable
obj
of typeany
:obj
to any other variable (except of typenever
)obj
e.g.obj.a.b.c.d
string
Code example:
In the example above, the
doSomething
function would compile, and both calls to it would compile, but of course if we run everything, it would fail in runtime.To battle this, you can use e.g. ESLint’s no-explicit-any rule, and in general only use
any
as a last resort