|
My experience with TS is totally on the backend. The biggest issue with typescript, and the reason I'll probably never reach for it for another large project despite having used it for a 50k line+ project, is that it's still, at its core, javascript. We've experienced a tremendous amount of pain because of this. - We've needed libraries which don't provide typescript typings. Like a good little typescript user, we have strict mode enabled, which means we have to build our own typings when we encounter this. We've had library maintainers reject our typings we provide back to the community. We've had library maintainers say they have no desire to provide any support to typescript users because "its a fad" and "ts is just js, you don't need types, live with it" (not joking, not exaggerating). - All of the good high-level JS frameworks and architectural libraries are built for, well, JS. So, you want to adapt them for TS, but the JS idioms are so core to their design that you throw away any benefit TS gives you. Express is a good example; you might have middleware which parses a bearer token and attaches it to the request. When the request hits the next middleware or request handler, its just a normal request. There's no way to assert at compile time that "this middleware came before, the request is now a new type with this extra field". Express, Apollo, the list goes on. - Further, the community around creating Typescript-native libraries is exceedingly small and, as far as I can tell, dying. I'm talking libraries that are made for typescript and make use of the true end-to-end compile-time verification typescript gives you. TypeStack is probably the leading organization (on GitHub) which builds (amazing!) projects like this, and their velocity is slowing down. TypeDI hasn't seen an update in nine months. Vesper was an incredible web framework that hasn't been touched in a while. There's nothing available for MongoDB if you use that. Basically, you're stuck with JS native projects 90% of the time, and about 10% of those don't have typings available. - We've had NPM libraries redefine the Error global. This isn't caught at compile-time, obviously, and totally borked the error responses our API sends. - It's a constant uphill battle with the `as` keyword. I wish there was a way to easily ban it globally. You can essentially assert Anything As Anything, and if its not caught in review you'll end up with incredibly hard to trace behavior because "the compiler says this field should be there, why am I getting undefined"... well, its because sixteen layers down someone asserted a type without verifying its accuracy at runtime, and in 5% of situations its incorrect, and it missed review. - It's a constant uphill battle with lodash. We have developers on some teams that swear by it, and will scatter `_.get`s all over the codebase, then be startled when things break in unexpected ways. Typescript added the `!` operator, which is basically just an escape hatch, and it took us a long time to get everyone educated that this is, without a doubt, the dumbest thing Microsoft has added to the language because it doesn't actually do anything at runtime, it just suppresses a potentially useful compiler error. In summary, don't pick Typescript for the backend. It's an obvious improvement over JS, but you'll still be left wishing you hadn't. But I also don't know what the better option is for a high-velocity organization. Go and Rust are too unproductive. Ruby and Python aren't performant and aren't strongly typed. JVM languages are a pain. Elixir shows a lot of promise but the lack of typing concerns me. |
Why did you submit the types directly to them rather than to DefinitelyTyped? It's great when libraries maintain their own typings, but TypeScript handles the case where they don't really well.
>Express is a good example; you might have middleware which parses a bearer token and attaches it to the request. When the request hits the next middleware or request handler, its just a normal request. There's no way to assert at compile time that "this middleware came before, the request is now a new type with this extra field". Express, Apollo, the list goes on.
Yeah, this can be a pain. In situations like this, I've defined types like `type RequestWithAuthField = Request & {auth: AuthField};` and then in handlers that I knew came after the auth middleware, I'd immediately cast the request parameter to that type. It is an escape hatch and I'd prefer to not have to do that though.
>Further, the community around creating Typescript-native libraries is exceedingly small and, as far as I can tell, dying.
Besides that they're sure to avoid TypeScript-unfriendly APIs (unlike Express), I don't think there's much difference between a library with good type definitions and a TypeScript-native library. (Maybe the library could have bugs that it being authored in TypeScript would have avoided.)
I don't fully disagree with your points. There's a lot awkward about TS on the backend, but at the same time there's not much quite like it.