The problem is that the deserialization is untyped. It's just a Javascript object. Usually, the first thing you'll do is to cast it to a typed Typescript definition, but there's no way to guarantee that it actually fits that definition. No type information exists at runtime. It's used solely to check the code itself during compilation.
So any time you get data from outside of your program (over the network, from a database, out of a config file, etc.) you just have to hope that it actually fits the type. You can write code to check it... but you have to write that code yourself.
There are libraries you can use. For example, you can use a Data Description Language with a simpler type system, which usually suffices for the kind of data you want to serialize. Then you can use that to generate both a Typescript definition and a runtime type checker. But that's inelegant, and not standard, so every project is different despite it being something everybody needs.
JavaScript has a very simple built-in json reader/writer that does no type checking and returns some nested dictionaries or arrays. If you want more, you need a library (for example zod). Or you just trust the data to have the right shape.
I imagine C# and other Java-esque languages have something similar. But Typescript deliberately avoids that, and it would be difficult to get it to do so.
Additionally, because it is impossible to emit these types to the runtime, even in the form of a string naming the type, you cannot validate input to an API using generics. If you have, say, a class which will run a query for a record based on the keys in that record:
class Query<T>{
filter: Array<keyof T>
};
The typescript compiler can validate if a string in "filter" is, in fact, a key of the class T. However, you cannot write any decorator on this class to make the information of what keys exist on T available at runtime. Therefore, to write such a query interface and validate it at runtime, every record which can be queried with this object must have its own specific "[Record]Query" class written with its own decorators to validate every possible input, usually with the valid keys hardcoded into the decorator.Because there is no real relationship between the class and the runtime validators, this makes validation more error prone, because it relies on either human beings to consistently remember to add new fields to the relevant decorators, or a custom linting tool to be written which basically pre-processes the typescript source using the typescript API. This is, again, writing code to account for the lack of features in the framework.
WHY do we put up with this crap? We have had Java and C# for a long time, and they solved these problems a long time ago. Why are we writing Typescript on the backend?
The simple answer; it is easier to find javascript developers than it is to find java developers. Bootcamps don't teach Java or C# because there's more react jobs out there. Bootcamp grads are not generally coding enthusiasts and so re-tooling is difficult for them and they don't generally do it for fun. So, we decided to write our backend in Javascript (or Typescript) as a labor decision, not a technology decision.
With c# I've seen openapi interface generators that don't validate properly, only basic deserialization. I've seen dto's that are deserialized wrong due to lacking null checking attributes. I've seen the way put requests are misused due to how difficult it is to separate null and undefined in patch requests. I've seen dto's with all nullables due to the lack of union types. Maybe I've yet to see a good c# codebase, but I certainly prefer typescript over the above.