TypeScript only provides the illusion of type safety. At runtime, it’s just plain old JS where all bets are off. I’ve been bitten too many times. As long as there’s no native TS runtime were stuck pretending we have type safety.
I'm not sure I understand this comment. I can believe that typescript might not be typesafe, but what does the runtime not having type information have to do with that? If a compiler only accepts sound programs, then it shouldn't matter whether the runtime actually knows about the types or not (unless you want to do some sort of reflection, but allowing that is arguably _less_ typesafe). By that logic, you'd also have to consider anything that compiles to native code also not to be typesafe, because there sure aren't types in assembly.
For me, the main place the issue above rears it’s head is in data retrieved from an API. A strongly-typed language would fail loudly and predictably if a blob of JSON didn’t match the expected format. In some situations, that’s preferable to having invalid data work it’s way through the system, or having to manually implement type checking
I’m a fan of the Runtypes library for this use case. If you design your typescript interfaces using the library (admittedly a bit magical to me) you can get runtime errors if API data doesn’t match the expected type
You can use guard functions to validate external data. This same problem comes up in other statically typed languages. You can't guarantee that external data will be in the shape that you expect so you must validate it. If you're just taking your `unknown` API response and immediately casting it to your desired type then that's your fault, not TS
Yeah, ideally JSON.parse() returned `unknown`, but unfortunately that was added after the current type definition was already in widespread use, and therefore would be too much of a breaking change. Perhaps some day with a compiler flag.
There are a lot of core language types that could substantially improve safety (not to mention inference), with anything explicitly `any` being obvious candidates. I feel like this should be a compiler/lib option for those of us who would opt into the breaking change for increased safety.
But since it isn’t, augmentations may be an option. I’m not at my computer right now so I can’t verify that this would work, but you may be able to add an overload that returns `unknown`. Unfortunately with overloads, I’m pretty sure the built in types would be selected first without some additional specificity.
This feels like a good opportunity for a safer standard library package wrapping globals, DOM, Node built-ins, etc.
Unfortunately no: that only errors when you do not add a type annotation and TypeScript can't infer anything better than `any`. JSON.parse(), however, is explicitly annotated to return any. See [1].
You could provide your own lib.d.ts with `unknown` type returned for JSON.parse. I'm almost surprised there isn't a standard stricter lib.d.ts, though I suppose that linters fill enough of those needs with their rulesets.
Looking at GitHub issues surrounding it, it doesn't appear that there has been an explicit ask to switch the type signature to use `unknown`, probably the next closest was this one, where `unknown` is mentioned in a comment as a possibility but the comment is not addressed in follow up discussion: https://github.com/microsoft/TypeScript/issues/26993
It would be a big breaking change though at this point, so they would probably be hesitant to do so. I thought I saw somewhere there was a proposal to add it as a compiler flag to treat all uses of `any` in lib.d.ts as `unknown`, but I have no idea what happened to that proposal and quick searches don't turn up anything.
Ah, I see my mistake. I have been getting warnings about JSON.parse, but they are coming from eslint@typescript-eslint/no-unsafe-assignment. It partially serves this purpose, but probably not as comprehensively as actually having it return unknown would.
They are probably talking about accepting serialized data coming from a network request. Typescript doesn’t help at the edge if you just cast your JSON blob into your application model.
That's why you actually have to validate at the edge. TypeScript doesn't really change that fact. It only makes the decision of how you handle it explicit.
I feel like this is the same argument that people give when trying to justify not writing tests. Tools like type-checking and automated tests aren't a magic forcefield that will shield you from any and all runtime bugs. But they do protect against a good percentage of them, and as another commenter pointed out...70% type-checking is better than 0% type-checking.
The point of type-safety is to be able to describe more succinctly what the _programmer_ expects out of the code. As you said, when you get to production, "all bets are off". This still applies with any type-safe language. Types don't exist when you're compiling to machine code, either.
> 70% type-checking is better than 0% type-checking.
I'm not sure it is; the most dangerous things in programming are assumptions that are almost correct, so you don't notice the problem until it's too late. An error whose symptom is an Int actually being a String several files away from where the mistake actually happened can be harder to diagnose than the same error in a untyped language, just because it's so contrary to your expectation.
TypeScript provides both of these benefits just fine. Runtime types give you better runtime errors, and less undefined behaviour, but plain JS (or TS compiled to JS) does that bit just fine. TS types persisting at runtime could maybe be nice for certain use cases, like more powerful reflection, but that’s not type safety.
What’s a concrete example where your program would be more safe if TypeScript types were represented at runtime?
In my experience nobody is "pretending"; within epsilon of everyone knows this, and the experienced folks are writing code against it with something like `runtypes` or `io-ts` to sanitize on the edges of their application.
True, but it's the benefit of incremental adaptation of types on an old code based.
It also encourages better architecture in my opinion, as it forces you to reduce the importance of something being an exact type... As it's not strictly enforced at runtime.