Hacker News new | ask | show | jobs
by lhorie 2494 days ago
> your example looks like casting `void *` in C

In practice, that's a pretty good approximation, except that in C, such a cast sticks out like a sore thumb, whereas something like `const o:Foo = getSomeDataSomehow()` looks the same for both a matching concrete type as it does for a cast from `any`.

The thing with type systems like Typescript/Flow is that there are both structural types and nominal types, and _some_ ability to refine nominal types based on their structures, but then people think they can extrapolate that limited capability to ends that the type system doesn't really support.

The return type of JSON.parse can very neatly be expressed with a recursive ADT (e.g. something like `type JSON = string | number | boolean | null | {[key: string]: JSON} | Array<JSON>`), and TS/Flow do have the ability to refine ADTs, e.g. `if (typeof x === 'string') x.toLowerCase()` is perfectly sound. Why that's not the default is a bit mind boggling IMHO.

What doesn't make sense is to assume one can cast a generic structural type to a random concrete nominal type using the "I know better than the compiler, let me cast" escape hatch mechanism, and then simultaneously omit the runtime refinement checks that they are responsible for writing, when they voided warranty through the cast.

But it's a heck lot harder to explain that they are writing unsound code in this case, because "hey look my type coverage is high, it must be sound!"

TL;DR: TS/Flow aren't silver bullets, but as usual, people tend to cling on to the brand as a social proof of "safety", rather than actually taking the time to understand what actual type safety is all about.