It is meant to be a much better alternative to Javascript while dealing with the fact that the underlying engines use and existing programmers were used to Javascript.
That said I absolutely enjoy TypeScript, but that might be because I suffered from having to deal with Javascript from 2006 until TypeScript became available.
god-awful code.
But the learning curve... no shit.
It would be cruel for me to force ReScript onto the team because they'd all need to reskill. I could only use it for a private project and then hire exclusively for it afterwards
The reskill problem is of similiar difficulty with learning a new framework I think. Especially because the language is rather simple compared to typescript (which is also its strength).
I do understand it is an uphill battle. The whole nobody get's fired for choosing IBM thing. The language is still unproven in the general perception. I do think that when it comes to libraries and frameworks I see a lot of developers choose new unproven stuff, more then they do languages.
https://v11.rescript-lang.org/docs/manual/latest/migrate-to-...
Writing TypeScript is better than JavaScript, but the lack of runtime protection is fairly problematic.
However, there are libraries such as https://zod.dev, and you can adopt patterns for your interfaces and there's already a large community that does this.
From experience, we end up with a mix of both Zod and types and sometimes types that need to be converted to Zod. It's all quite verbose and janky.
I quite like the approach of Typia (uses build-time inline of JavaScript), but it's not compatible with all build chains and questions abound on its viability post Go refactor.
In my code, everything is a Zod schema and we infer interfaces or types from the schemas. Is there a place where this breaks down?
1. Router
Tanstack Router: Supports runtime validation libraries such as z0d. So I have routes such as example.com/viewer/$uuid/$number, it should 400 if those aren't actually validate uuid and numbers.
React Router: Supports Types, but every type is a string because, well, they technically are, but this isn't useful in practice in my opinion. There are 3rd party libs such as: https://github.com/fenok/react-router-typesafe-routes
2. API
Lets say you're making your API public to clients you can't trust to send the correct data ( which probably also includes your own client ).
https://www.npmjs.com/package/express-openapi-validator
This library advertises validating both your input and your output
3. State
https://github.com/pmndrs/zustand/discussions/1722
4. Database
https://www.npmjs.com/package/prisma-zod-generator
5. Forms
https://medium.com/@toukir.ahamed.pigeon/react-hook-form-wit...
6. ENV
https://jfranciscosousa.com/blog/validating-environment-vari...
Obviously checks on the agent are primarily a DX/UX thing, whilst checks on the server step are also security controls.
I could forgive that.
The TypeScript case is more like "what if instead of checking the types we just actually don't check the types?".
The "impossibility" is just a trait of the type definitions and assertions that developers specify. You don't need to use TypeScript to understand that impossibilities written in by developers can and often are very possible.
I wrote some code that iterated over lines in a file or something and passed them to a function that took an argument with a numeric type.
I thought this would be a great test to show the benefits of TypeScript over plain JavaScript: either it would fail to compile, or the strings would become numbers.
What actually happened was it compiled perfectly fine, but the "numeric" input to my function contained a string!
I found that to be a gross violation of trust and have never recovered from it.
EDIT: See https://news.ycombinator.com/item?id=46021640 for examples.
You're just stressing that you had a fundamental misunderstanding of the language you were using when you first started to use it. This is not a problem with the language. You simply did not understood what you were doing.
For starters, TypeScript does not change the code you write, it only helps you attach type information to the code you write. The type information you add is there to help the IDE flag potential issues with your code. That's it.
See how node introduced support for running TypeScript code: it strips out type info, and runs the resulting JavaScript code. That's it.
If your code was failing to tell you that you were passing strings where you expected numbers, that was a bug in your code. It's a logic error, and a type definition error.
Static code analysis doesn't change the way you write code. That's your responsibility. Static code analysis adds visibility to the problems you're creating for yourself.
Some tools are more perfect and more useful than others.
Typescript's type system is very powerful, but without strict compile-time enforcement you still spend a lot of effort on validating runtime weirdness (that the compiler ought to be able to enforce).
You don't have to rewrite your whole codebase to start using it. It grows horizontally (you add typed files along the way) compared to typescript which grows vertically (you enable it with Any types).
The point is that we don't have to move back to plain js. We have learned a lot since typescript was created and I think the time has come to slowly move to a better language (and ReScript feels the most like Javascript in that regard).
That's something that you own and control, though. Just because TypeScript allows developers to gently onboard static type checking by disabling or watering down checks, that does not mean TypeScipt is the reason you spend time validating your own bugs.
Unfortunately, taking an ecosystem-wide view, it means exactly that. If one of my dependencies hasn't provided type stubs, or has provided stubs, but then violated their own type signatures in some way, I'm on the hook for the outputs not matching the type annotations.
In a strict language, The compiler would assert that the dependency's declared types matched their code, and I'd only be on the hook for type violations in my own code.
What I mean to say is - TypeScript isn't proof.
https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAllApg...
https://www.typescriptlang.org/play/?#code/DYUwLgBAHgXBB2BXA...
TypeScript designers insist they're ok with it being unsound even on the strictest settings. Which I'd be ok with if the remaining type errors were detected at runtime, but they also insist they don't want the type system to add any runtime semantics.
Second example: that's the point. If the compiler can't prove that x will be initalised before the call it should reject the code until you make it x: number|undefined, to force the closure to handle the undefined case.
val items: MutableList<Int> = mutableListOf(3)
val brokenItems: MutableList<Any> = itemsx is clearly defined to be a number. The compiler should produce an error if the delegate captures x before it has a value assigned.