Input > Enumerable.Map(Input, type-speccd functionA) > Enumerable.Map(Input, type-speccd functionB)
Here's just one very simple example, there are many more. I've checked all the strict mode options and this appears to still "typecheck".
var x: {a: number} = {a: 1};
var y: {a: number|string} = x;
y.a = 'FAIL';
var n: number = x.a; // not actually a number
Source: https://www.typescriptlang.org/play/?noUncheckedIndexedAcces...1. TypeScript doesn't aim to have a sound type system. i.e. there may be things the type system accepts that are actually unsafe.
2. this is more of an issue with mutation. If those properties were marked `readonly`, then the assignment of y.a wouldn't work at all. You can also encapsulate mutation behind functions with your intended types.
I tend to write TypeScript in a "functional" or "immutable" way, and in this case, most soundness issues come from things like array index access, which can't really be solved without dependent types anyway.
With that said, TypeScript still gets one quite far *despite* soundness not being a goal of the type system. The problem is that writing imperative, mutable code will make you go through (intentionally!) unsound covariance of types. Similar issues exist for code with side effects, since TypeScript has no way to encode effects in the type system. This is why some language communities settle on ideas like "functional core, imperative shell", where the ultimate goal is absolute minimum amount of code involved in side effects and mutation, while everything else is designed to be easy to test (and, ideally, expressible with a sound subset of your type system).
It's actually a very powerful tool when used thoughtfully. Although it wasn't the first structurally typed language I tried, it's the one that made me fall in love with structural type systems
It Catches: Mismatched function arguments, missing object properties, and typos in variable names.
It Misses: Invalid JSON from an API, unexpected database outputs, and bad user input.
I would also just like to point out that the "It Misses" your robot pointed out aren't actually flaws with TypeScript but flaws with JavaScript.
I used to be a bit of a pragmatist when it comes to strict mode, but over the years that has subsided, nowadays I think it is plainly obvious that all Typescript programs should use strict mode unless there's a damn good reason. And I'm not sure there are any legitimate damn good reasons.
True there is no ability to forbid an explicit-any type declaration, though.
The real problem with Python is the inexpressiveness of its type system and the mess of typed dicts, dataclasses and pydantic classes.
TypeScript may fail narrowing here and there or require a superfluous assert, but usually writing properly typed code, especially with zod, is the path of least resistance.
You probably have the same logical type duplicated in 3+ different places (at least partially), including inline casts using type literals like "maybeCat as { meow(): void }"
Elixir is always been sort of a "typed dynamic language" due to how baked in pattern matching is. Any good Elixir developer has always been thinking about types anyway, it's almost impossible not to.
I’ve toyed around with it a handful of times and I really like it. I like the clojure-ey immutability and threading operators and such. And of course I’ve heard so much about the magic of the BEAM and the phoenix framework. But between typescript and clojure I’ve never felt like I needed anything else.
But if the type system is pretty good, that’s a huge plus over clojure in my book.
I don’t think JavaScript’s syntax was ever designed with the idea that TypeScript would one day exist. Yet somehow it feels like it left the perfect open spaces for TS to later occupy.