Typescript is a lot easier to deal with if you stop treating it as optional and do it from day 1. Avoid using the any type and things fall in to place. If it's tedious, you're probably doing something wrong or sub-optimal. Or you're just dealing with a bit of hairy old javascript that probably needs a bit of refactoring in any case.
IMHO we're reaching the point where typescript (or similar languages) should be used by default over untyped javascript in professional environments. It's like having tests, which are also not generally considered optional. I've been in CTO type roles and already insist on it when I can. I don't think I'm alone in this and many organizations only do typescript at this point.
>If it's tedious, you're probably doing something wrong or sub-optimal.
>IMHO we're reaching the point where typescript (or similar languages) should be used by default over untyped javascript in professional environments. It's like having tests, which are also not generally considered optional.
Gotta love JS community. It flip-flops on some major aspect of system design roughly every year, yet people continue to arrogantly spout their opinions about system design as if they had been correct about everything all along. Not "these are the benefits, these are downsides" not "I've improved in X by using Y", always "nobody needs X, everyone must do Y, and if you're not doing Z you're wrong and unprofessional". Zero self-awareness.
I'm always confused when someone calls out a large group of people for not having a consistent opinion.
Gotta love Americans! They flip-flop on major aspects of policy making. One day they spout "ban guns!", the next they say "If need be I'll defend my right to bear arms with violence!"
I recently started doing a lot of work with React and JS, coming from years of native application development.
It does seem like the general "trend" or "consensus" in the JS community is very inconsistent and easily influenced by a few well-linked blogposts or highly upvoted StackOverflow answers, which themselves are very often written by people with honestly limited software and system engineering experiences. Sometimes a "best practice" in 2017 would be considered awful and "should avoid" in 2018.
Btw Americans opinions do flip-flop on major aspects of policy making, due to all reasons from media coverage to current events. Look at American's support for the Iraq War for example. Herd mentality, leader worshipping, cargo cult, etc are just as prevalent in public policy discussion as it is in software development community.
JS/React, due to having a community that's larger and arguably less experienced than most other community, just show more of the same symptoms.
Well it's the generalisation about a large community that is the problem, whether put arrogantly or not (the arrogance only makes it harder to scroll past and ignore). GP put it well:
> I'm always confused when someone calls out a large group of people for not having a consistent opinion.
The way I read OP was "given that X flip-flops, it's weird some people talk as if X is constant" (i.e. acknowledging the inconsistence in opinions and, further, calling out those that don't)
> Gotta love JS community. It flip-flops on some major aspect of system design roughly every year, yet people continue to arrogantly spout their opinions about system design as if they had been correct about everything all along. Not "these are the benefits, these are downsides" not "I've improved in X by using Y", always "nobody needs X, everyone must do Y, and if you're not doing Z you're wrong and unprofessional". Zero self-awareness.
Gotta love HN community. It flip-flops on other's people opinions every day, yet people continue to arrogantly spout their opinions about those opinions as if they had been correct about them all along. Not "these are the benefits, these are downsides" not "I've improved in X by using Y", always "nobody needs X, everyone must do Y, and if you're not doing Z you're wrong and unprofessional". Zero self-awareness.
(1) OP said "we're reaching the point" which is clearly a nod to the maturing TypeScript ecosystem, which is why they changed their opinion over time. I've also changed mine for the same reason.
(2) Sometimes there are such massive benefits that downsides are almost irrelevant. TS has instantly saved me from hundreds of bugs, some of them nasty, and only caused me grief a few times. There are few downsides. I agree with OP that as a default, TS should be used over untyped JS in professional environments.
The problem IMHO is the old adage of the devil being in the details. I see a lot of engineers talking about things like deriving types from enums, and meanwhile the type system will merrily let you do this:
It feels like people are lulling themselves into a false sense of security by making increasingly complex self-consistence schemes via type utilities, but they just shrug when I point out that the foundation of the scheme is still unsound.
I've run into issues with TypeScript that forced me to cast string literal types to themselves before it would compile. So literally this code:
'x' as 'x'
Nothing else would work, we were simply forced to do that. Avoiding `any` in all cases is simply not possible, as Redux will require you to use it at least once as of the last time I used TypeScript, which wasn't that long ago. Type discoverability for libraries was such a massive hassle that I don't see how other TS devs have gotten around it. Not everyone can use Visual Studio.
I don't find TypeScript's type system to be anywhere close to sound. Instead, it feels like it lies to me a lot. At least it has HKTs now, so that's nice. But I ended up moving to ClojureScript (and a little PureScript) and I've never looked back.
> It feels like people are lulling themselves into a false sense of security by making increasingly complex self-consistence schemes via type utilities, but they just shrug when I point out that the foundation of the scheme is still unsound.
I don't agree with this premise. Assembly is an untyped language but you can build things on top of it like Rust or Haskell, or you can write in C and cast everything to a void. I do* agree that validating the types of values is a hard problem in any language and that JSON.parse should be typed as `unknown` these days (although there are also reasons why it should not do that due to casting), but it makes complete sense IMO. JSON.parse could return any valid JSON value. The compiler doesn't know the structure of the string provided beforehand.
The difference between something like Rust->ASM and TS->JS is that the former actually emits runtime machine code to deal with e.g. an Option, whereas in TS, the compiler is happy to emit the exact same runtime code for both `const o: Foo = JSON.parse('null')` and `const o: Foo | null = JSON.parse('null')` without throwing a compile error.
Rust makes it exceptionally "hard" to write unsafe code, by making it blindingly obvious when you're doing it, whereas in TS it can be very challenging to spot unsoundness, especially considering that the audience of the language is not type system scholars.
I won't argue that getting strong code in TS is easy but I definitely don't think it's as hard as you're making it out to be. That being said, it's more of a limitation of the underlying execution context in my opinion, and you have to realize that it's not just jS that's out to get you but the whole ecosystem. But that's the expectation in all languages, even in Rust. In production optimizations all bets are off and you're on your own if your app dies.
For example of what I mean by the platform being out to get you, the second parameter to JSON.parse is a recovery function. So it'll happily parse anything and return anything. Again, should be "unknown" but it was typed before unknown was a thing.
JSON.parse('{ "fooDate": 123456789 }', (key, value) => !key.includes('Date') ? value : new Date(value));
JSON.parse('123456789', (key, value) => new Date(value))
I actually just learned about this today, since I was doing some digging. Horrifying stuff for a typed language to get around. FWIW, we've made it an explicit lint error to use the `any` type, and enabled --noImplicitAny and --noImplicitReturns. That's been pretty solid for our codebase and because I/O is limited to a few edges, we can call out those edges for type guards.
As far as I don't like the "Angry Lisp Drunk Haskell" version of TS e.g. loads of `<` and '>' mixed with functional concepts from half of the Haskell, which makes understanding code harder than necessary.
Typescript is one of the rare good things in javascript. You have to understands JS, because there is always gun pointed at your foot, waiting to blow you away.
Still TS makes this gun, a little bit harder to trigger. You cannot just write Java in it and cross your fingers.
Typescript gives you pretty good docs most of the time for free. Only problem is when author of code used `any` type or `Angry Lisp` version of it. Interfaces, enums and field access and typedefs are godsend.
In defense of TypeScript, your example looks like casting `void*` in C. Well typed `JSON.parse` would be pretty complicated and require runtime-machinery.
Only thing I dislike about TS is lack of proper `optional` types. I know the '.?' operator is coming, but still.
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.
That's a very good point! Although I find the example to be rather exaggerated, I see how these types of errors can happen when you're dealing with very complex types. In the post, I also talk about how wrongly-typed dependencies will compromise type safety, which can be another source of errors.
Nonetheless, I think that TS can catch a lot of errors and will help documenting your code - and that's a very good thing on its own.
You can't cast from a HashMap to an Animal class in Java, so in that sense Java is sounder. But you can still do `Animal a = null; a.walk();`, so in that sense, Java isn't sound.
Correct me if I'm mistaken: I think you can indeed cast from HashMap to Animal and it will happily compile:
```java
import java.util.HashMap;
class Main {
class Animal {
String sound = "roar";
}
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
Animal animal = (Animal) (Object) map;
System.out.println(animal.sound);
}
}
```
At runtime, this will crash with the following Exception: `Exception in thread "main" java.lang.ClassCastException: class java.util.HashMap cannot be cast to class Main$Animal`, but it will satisfy the type system.
this is why I wish typescript added runtime checks in development. Worst case, it would throw a TS error in your browser any time a function call or a network request didn’t match what you typed
but I can’t add checks to the couple places I let users upload json for a form builder, for instance. No way to validate json with typescript natively. That would be awesome
Or maybe there are multiple voices with various opinions. What has remained constant for a few years is the volume of condescending comments that any discussion about js gets on the sole basis that people are discussing js.
OR JS developers are talking to their community and not you. The only language we can really use has evolved over the years to meet the problems that have cropped up. In the past a scripting language was enough to complement the HTML document it was served with. Now that web apps are expected, we need a real programming language and strategies.
I am a backed developer 90% of the time, but I currently have to work on a Typescript / React application that an agency did for us - cleaning up the bugs.
Is there a good resource to demonstrate how to get around the problems you get with typing? At the moment I am using @ts-ignore to get things done. (Saying "you are probably doing it wrong" isn't really very helpful).
- turn off strict flags, turn them on again after everything compiles in non strict mode
- ensure you have the correct typings for the libraries you're using. Some libraries include them, others require a @typings/xyz dependency. (E.g. React and ReactDOM)
- try to hunt down the root errors. Much like C# or Java, one error can lead to hundreds of compilation errors down the line, but fixing the first root error can also make all of them go away in one fell swoop.
- try to keep things simple and non dynamic. Typescript is very flexible and powerful, but think twice before you use crazy constructs.
- enable emitOnError. It will allow you to test while you refactor, even though typescript complains.
- ask yourself: if it works and typescript does not compile, is it because typescript can't understand or is it because typescript is seeing possible issues you've not taken into account?
- don't think of typescript as something to get around of. Think of it as a helping hand that will guide you in your daily work and prevent a whole swath of runtime errors, but it needs to be fed with information about your data structures and libraries to work properly.
Typescript is really unlike C# or Java though, error cascading is a much much bigger issue in a structural type system than a nominal one, where the x method not implemented by class C will trigger whenever you try to call the x method (in a nominal type system you just get the error once, the typescript team could probably do a better job with this by investing some resources into the problem).
I am trying to remember the error that I got. I think it was complaining that the variable being passed to a function may be a null rather than the type that was specified.
Doesn't sound like a problem you get with typing, sounds like it was actually catching what could be a runtime error in production. If the variable is possibly null but the function you're passing it to expects it to not be null then typescript is pointing out what could be a runtime error if you don't explicitly handle that case (if (!foo) ...)
Or worst still, an or undefined, often found in class variables.
this.foo: Thing | undefined = undefined;
Easy way to sidestep this error is > if (!arg) return;, but it’s really a code smell of a larger, design issue. As in, the function shouldn’t really be depending on an optional value.
I would highly recommend turning off no implicit any of you haven’t already — implicit any _will_ make it harder for you to figure out how to solve all the typing issues because it will make it significantly harder for you to understand the way type inference works by inspection. — E.g. if you are learning the typescript by looking at example code — a block of code which only types correctly because the compiler implicitly inferred the any type somewhere within can very easily lead you very far astray from understanding how the type system actually works ...
You might have to fix a bunch more errors but at least those are easy to fix (just add any explicitly to the type — or better yet, add the real type if it’s obvious) ...
Eventually if you force yourself to figure things out at every painful step you'll get there. We still have quite a few ignores but the type safety is actually so so nice; you almost know your code will work 90% of the time if the red squiggly lines disappear.
I'm really going to struggle if I have to go back to normal JS now which says something!
Totally agree, and I suggest turning the strict option on (in tsconfig). There are a plethora of additional bugs that the TypeScript compiler will catch for you, but they're turned off by default (though I think create-react-app turns it on now?). For example, making sure class members are initialized. For a greenfield project I see no reason not to turn it on; TS obviously leaves it off by default because it would make working with older code that much harder.
I'm with you on this.
In my opinion, TypeScript can really get in your way when you develop in a way that's not TypeScript-friendly, but you'll benefit heavily once you start developing with types in mind.
Right, you can even think of static types as the base of a solid testing story. Totally agree. Kent Dodds has some great writing and courses which refer to this.
If it's tedious, you're probably doing something wrong or sub-optimal. Or you're just dealing with a bit of hairy old javascript that probably needs a bit of refactoring in any case.
No, it's going to be more tedious 100% of the time. Now, if that tedium helps you in the long-rum is up for you to decide.
Isn't it slower than node during compilation time though? I understand that there's a slight overhead on the initial setup of it already which I can plow through it but the hit on performance would be concerning cause there's already an overhead of webpack transpilation.
Make a <library-name>.d.ts file somewhere in your repo, and make its contents be `declare module 'library-name';`. This will make Typescript treat the library and all of its exports as any-typed. If you find it useful to, over time you can fill in the .d.ts file with actual type information, and then maybe submit it to DefinitelyTyped when you think it's complete.
They almost all do if you look hard enough, but it there really isn't one this is one of those edge cases where it's ok to generally just define it as an 'any' and save yourself some hurt.
It's that or take the time to make it yourself, but it might not be worth the effort, it's for you to judge.
Lose it or fix it (e.g. by writing some type definitions). If either is infeasible, put a fence around it and surround it with some layer. Most common libraries have type definitions at this point. The better ones actually maintain those themselves.
I've been writing Typescript with React for quite a while now and these are my feelings so far:
- Typescript type system is pretty awesome, and allows the expression of some things really elegantly; in particular, string literal types are quite cool for component props that feel "htmly", union and intersection types are great for making reusable/generic components and Partial<T> is cool for making typesafe component states (https://www.typescriptlang.org/docs/handbook/advanced-types.... is a great resource)
- on a related note, the Typescript docs are very comprehensive
- ... but docs for anything React related to Typescript (types of components, etc) are harder to come by
- for the actual UI code there is some time wasted getting type signatures perfectly correct, particularly for React and HTML components, but I've built up a bank of helper functions in this regard.
- using `any` nearly always comes back to bite you as you trick yourself into feeling typesafe
- there is always a tradeoff between having perfectly exact types and not writing 139587123598 interfaces; expressing, for instance, mapStateToProps, mapDispatchToProps and mergeProps to compose into component props, or the former as a subtype the latter is pretty fiddly to get right and imo not worth the extra code
- create-react-app typescript support has gotten pretty good now, but it's nigh-impossible to step outside their boundaries. For me, some older features of typescript I wanted that protobufjs generated typescript used was just not usable and I had to work around that with great difficulty
- nearly all packages now have type definitions for them which is sick
- at the end of the day, you can still resort to vanilla JS where typescript really, really gets in the way
> ... but docs for anything React related to Typescript (types of components, etc) are harder to come by
I just saw this linked today and didn't look it through entirely, but this React+Typescript cheat sheet looks very interesting for React-specific issues you might encounter with Typescript:
For example it explained an issue with the type inference for custom hooks that confused me somewhat earlier.
I really like Typescript so far, but you can easily encounter situations that are hard to figure out with only basic Typescript knowledge, especially when interacting with more complex libraries. This probably gets better with more Typescript experience, but it can be a serious speed bump as a Typescript beginner.
This is wonderful, and probably a better strategy than the one I've employed (e.g. hovering the onClick attribute of some component, seeing what type pops up in VSCode, copying it, and using that for my type, done!).
As a disclaimer, it assumes you use Twirp for any RPC impls, merely b/c that is what we used, and so it needs a config flag to turn that off + also eventual support for canonical GRPC-style RPC.
And also the docs/install/setup all need polished, but feel free to file issues / PRs if you try it.
I'm currently trying to revive a 2yrs old codebase written with TypeScript/React/Redux. I made the capital error of not checking in node_modules apparently or pinning versions (ie using yarn or npm shrinkwrap), as I now get tons and tons of type errors from dependencies on build. Problem seems to be that all the @types packages are somehow out of sync/broken/hell I don't know. I also don't have access to CI logs anymore so I can't figure out which versions it used to resolve to …
The @types definition packages for react-router, redux-thunk, etc. give me "error TS2605: JSX element type X is not a constructor function for JSX element". Most popular answer on GitHub is to rm -rf node_modules and rebuild, however that does nothing for me. I tried upgrading some @types selectively and triple-double checked that the resolved versions make sense, but nothing so far.
A codebase that used to build cleanly now throws ~30 errors on build, without any changes to it. Insane. Always pin your exact versions in JS land …
That error you're seeing could be because you have multiple @types/react packages in your dependencies. You really should have just one version of each @types/* package installed. If you use yarn you can use `yarn why @types/react` to find out if you have multiple versions installed. To resolve these kids of errors, I usually uninstall all @types/* packages and then install them all at once again. Alternative is to use yarn resolutions (https://yarnpkg.com/lang/en/docs/selective-version-resolutio...) to force a specific version of those dependencies.
To me it usually happens if I upgrade one of the @types/* packages, and then yarn/npm decides to install a separate version of its dependencies, even if I already have a different (compatible) version. There is no easy way AFAICS to tell yarn/npm that you really only want a single version of each @types/* package.
Same applies to most regular npm packages as well, for example you really only want a single version of "react", too.
@types versioning is strange because you never know whether or not the version of the types matches the dependency exactly. for example the case where the typed version has a minor upgrade (say due to increased type coverage), the types become out of sync.
the fix is to do away with @types and export types from the dependency itself so they are always in sync
I find after a fresh `npm install` (as opposed to `npm ci` which pays attention to package-lock.json) an `npm dedupe` almost always seems necessary. `npm install` by default still doesn't seem to work hard enough to avoid duplication, especially with @types/ packages where is often critical.
This is a perfect example of a developer blaming themselves when in fact the real problem is that the tool is too complex with too many moving parts and that's why it breaks all the time.
If you're a driver and your car keeps breaking down constantly, you wouldn't open up the hood each time, slap your forehead and say "It's OK, it's only the camshaft phase variator this time!"
You rightly expect the car to just work. If the car breaks often, it means it's shit; simple as that. The solution is to buy a better car. And there is no excuse in this case because plain JavaScript is 100% free.
Not blaming myself, just grateful for getting this mess fixed. I cannot imagine working with tech like this on a daily basis, I'd probably ragequit in half a year
Ah yep. I know how it feels to solve some random useless problem that shouldn't even exist in the first place. It still feels great (like solving any other real problem). But it's a psychological trap that makes you feel more invested in the horrible technology which created the problem in the first place.
It's like filling out useless bureaucratic government forms. You still get a hit of satisfaction once you finish filling them out even though deep down you know it was a massive waste of time and the whole process could have been much simpler.
Actually, that happens all the time. The way node resolves modules and npm installs them, it's entirely possible to have several different versions of the same dependency. If you use npm ls, you can see where they ended up.
You can come up with any number of excuses but the bottom line is that TypeScript adds complexity which creates a lot of different problems which plain JavaScript does not have.
Because JS apps never have package management issues, right? If you don't understand your tools, it will eventually bite you. Has nothing to do with TS.
It's not really excuses when the person doesn't understand what they are doing or why. The problem is entirely with how NPM downloads dependencies, not exactly typescript.
The OP is rightfully getting type errors due to conflicting type defs, why is that a bad thing?
Dependency types are definitely one of TypeScript's biggest weaknesses.
That said, it can be seen as encouragement to minimize your dependencies. The big ones like React will be kept up to date, so if you have lots of breakage you're probably using lots of smaller dependencies.
As for mitigation strategies, you can always just use "any" as a temporary stopgap, or you can even write your own type files for third part libraries depending on how big of a footprint those have in your own code.
I had similar errors using an updated TypeScript compiler on an older codebase. There was a lot of breaking changes between TS versions and you're either stuck with an older version or you have to upgrade everything (as @types packages aren't usually backported).
I guess people in the React community are already used to massive dependency bloat, constantly failing vulnerability reports, library version incompatibility, huge install times, huge compile times, source mapping issues, dependency issues and build + linking issues across various environments so I guess TypeScript doesn't add much additional pain.
But what if I told you that it's possible to build cleaner, more stable, higher quality apps without React, without TypeScript and without build steps and you can do it in half the time!
With plain JavaScript + VueJS you get instant (0ms) build time, few dependencies, fast installation, no vulnerability reports, no version compatibility issues, no source mapping issues, no environment compatibility issues, no missing type definitions, no need to waste your precious time on renaming interfaces and you can code everything in half the time!
And that's not all; for an unlimited time, this toolset is 100% free, not controlled by any mega-corporation and requires you to read less documentation.
It's time that you took back control over your life with plain JavaScript + VueJS. We guarantee you'll love it or your money back guaranteed.
Well, I myself am certainly interested in going down that route, eventually (vs AngularJS, or, yikes, Angular 2+),
BUT, it appears that’s not a popular opinion on a post about Typescript and React.
I don’t seem to encounter many type and name errors when writing js, but I guess there is an audience for compile time checking, willing to pay the costs, and the industry certainly caters to them.
Yep, it's a beautiful technology, total type safe heaven.
Two basic basic rules if you want to work with TS:
1: apply the 'any' type
2: tweak TSC config so it won't complain anymore
All companies I worked for in the past few years that use TS did this to keep TS 'out of the way'. And with that you completely annihilate the main benefit of using TS! I think it's hilarious and sad at the same time.
I'm curious btw how long TS will live, especially when you realize that within a few years we can write in virtually any language through WebAssembly. Good luck with it anyways.
> Yep, it's a beautiful technology, total type safe heaven.
If you do what you're actually supposed to do (check in package.json files) it is.
> Two basic basic rules if you want to work with TS: 1: apply the 'any' type 2: tweak TSC config so it won't complain anymore
Oof, no. As you say, you're basically removing all the benefits of using TS that way. Honestly it surprises me what a hard time people have with it, it felt entirely intuitive to me not long after starting to work with it, and now I don't think twice about it (except to be thankful for it)
> I'm curious btw how long TS will live, especially when you realize that within a few years we can write in virtually any language through WebAssembly
I think the benefits of this are wildly oversold. There will be some very performance-intensive areas where WebAssembly will be useful, and it'll be a boon to be able to bundle cross-platform libraries easily. But outside of that you're going to be adding complication for little real benefit, and good luck hiring a "Go web front-end engineer" to work on your brand new codebase...
This seems like an unrelated potshot at TS. It sounds like the person you're replying to didn't have a yarn.lock or package-lock.json, so 2 years later, they're pulling in different versions of every dependency. Of course things are going to break.
>> Two basic basic rules if you want to work with TS: 1: apply the 'any' type 2: tweak TSC config so it won't complain anymore
That should be at the top of every page on the TypeScript documentation website. It would save developers so much suffering.
>> All companies I worked for in the past few years that use TS did this to keep TS 'out of the way'. And with that you completely annihilate the main benefit of using TS! I think it's hilarious and sad at the same time.
I'm still looking forward to the TypeScript version when they invent dynamic typing and enforce it at compile time.
I have similar experiences. What I've found is that a team which can build a high quality application in TypeScript is generally capable of building an even higher quality application in JavaScript in half the time and with better, higher quality test coverage.
>> I'm curious btw how long TS will live, especially when you realize that within a few years we can write in virtually any language through WebAssembly. Good luck with it anyways.
I look forward to this glorious day. Short live TypeScript!
I do find it rather strange that strongly typed code with no errors, that builds correctly, can fail at runtime due to type errors. My JS oftentimes ends up nicer than my TS because I don't have pages of, basically, type-level administrative work.
I don't have this experience at all in other strongly typed languages, including very strong ones like Haskell/PureScript. The types there just work for me.
This can happen if handling data from a remote client. Type checking does not happen at runtime so you still need to do your own schema validation even with TypeScript. So IMO it adds almost no value.
Disappointed that the article alluded to but never explains why classes should be avoided in Typescript. (Which I agree, btw)
If you’re used to classes, it’s really tempting to create classes for your models. But in Typescript, which gets compiled down to plain old JavaScript, you spend a lot of your time dealing with JSON and plain old JavaScript objects (POJO). These don’t have methods. These don’t have private members. These don’t have constructors.
You actually don’t need any of that. You just want type safety around your JSON and POJO. That’s why more often than not, you’re going to be using interface.
I’m not saying never use classes. But don’t use classes to define models. Use classes for controllers.
I think you missed the part where they said you don't need classes for POJOs and models i.e. things which only contain data and do nothing else. If you want methods that operate against the inner data of an object then totally write classes
It’s just that they started their post by saying that classes in Typescript should be avoided. A message "x should be avoided" is very different from "you don’t need x". Compare the statement "you don’t need redux", which has been incorporated into titles of numerous blog posts, with the statement "redux should be avoided".
I still don't hear a good argument as to why you should't use classes for your model.
I use [serializr](https://github.com/mobxjs/serializr) to convert json to my classes, and afterwards I can have all the deligtful methods I want. :-) I love the code clarity this gives, methods are where they are most logical, instead of on some helper object.
Method implies use of this, which implies they are probably not first class - such would be functions cannot be passed or returned, as the this reference won’t be bound if called as a function.
There's a historical reason why classes are used a lot for models. It's up to you to decide if this reason applies to your project or organization.
A few years ago there was a dilemma between ES6 and TypeScript. A lot of JS developers chose to stay away from TS in order to follow the ES6 path instead. Then TS harmonized with ES6. But it retained a reputation for being an incompatible alternative to ES6 rather than just JS+type annotations. However, if you write classes, they look just like ES6 classes plus type annotations, allaying concerns.
I see why you're missing an explanation on why classes should be avoided.
In fact, your opinion is very similar to mine - I also think that, especially in the context of the FP-influenced React, data should be separated from behaviour - I mean how exactly are you going to preserve immutability using OOP? React just works a lot better when there's no self-mutating objects.
Could you elaborate on why you think that classes should be used when implementing controllers? I tend to think of controller classes as singletons, which kind of contradict using classes in the first place.
Then what do you do about the old version of the mutated object? There will still be references to it in other parts of your object model, which will now be out of date. I think that the concept of objects preserving identity is central to OOP, which simply contradicts immutability.
Why would you somehow pass objects around that are not "final"?
I mean the objects that get pinned here and there are specifically designed to serve as wrappers for some state, hence they encapsulate that state, and thus they are mutable. (For example a DB connection/manager object, a service registry, a cookie/localStorage repository.)
Where the immutability usually helps is with shuffling data around, parallel computing (concurrent access, no need to lock), etc.
I've done 2 commercial Typescript + React projects so far (along with few side projects using what I knew that time + what I want to try). My experience been:
- Discourage 'any' but not been afraid of using it when must. I think it as the 'technical debt spelled out': when you want to put down an 'any' and get on with what you're doing, by all mean, but remember its existence and make sure the team is well aware. If the usage going to persist (example, using a library without @type), then you treat it as JS and have appropriate amount of validation around the occurrence.
- Usage of "?" and "!" covers more scopes with less lines of code. For hobby/one-man project, I found their usages no-brainer, however I'm nervous when it comes working in a team of various experience level.
- I've had real headache typing API responses. On one hand, you have absolute no control of others' code quality that you might as well have "number | string | null | undefined" for all parameters. But doing that almost defeats the purpose of typing it, so I'll need to use my educated guesses and judge reliability of each known parameters in responses.
- TypeScript version of 'create-react-app' projects builds slower than its JavaScript counterpart without ejecting. It took 2~6 seconds per build while IIRC, it was <2 seconds for JS. I was keen to find a solution for this, but after a while it grow on me and I simple stop save after each line of code.
- JSDoc is still relevant in TS code. It is great to document event emitters, exceptions etc.
- tslint and prettier are must in my projects in order to retain sanity for unnecessary discussions around coding style.
Thanks for the link, I am currently doing something similar without knowing TS implicit behavior.
With 1st given example in the link, I forced on a style to such:
```
function doSomething(x: number | string) {
if (typeof x === 'string') {
doSomethingForString(x)
}
// Never do catch all to assume all non-strings are numbers
if (typeof x === 'number') {
doSomethingForNumber(x)
}
// Per transpiler rule, you not meant to be here, so error throwing is appropriate
throw new Error('Unexpected type')
}
function doSomethingForString(x: string) {
// Code
}
function doSomethingForNumber(x: number) {
// Code
}
You're probably tired of hearing this by now, but ESLint is the "official" way to go with TS from this point on. It has better integration with Prettier too, since Prettier formatting differences shows as errors/warnings.. and you can share JS/TS rules if needed.
> Discourage 'any' but not been afraid of using it when must.
I've found that I rarely need `any` anymore. My solution has been `@ts-ignore` (and a warning coming out of tslint for that, to clean up as better typings, etc. arrive) and the `unknown` type, which better encapsulates the "valid" uses of `any`.
> I've had real headache typing API responses.
Swagger and generated library code has made this a lot better in my neck of the woods. It isn't perfect, of course, but it's incrementally better. (Obviously, if you don't own the API, it's harder. Though I've also defined Swagger docs for third-party APIs in the past and generated my own clients, if I understood them well enough.)
I do not work for apollo but I completely concur! I use absinthe on the backend with apollo on the frontend.
incidentally, there any chance of you guys releasing an ios/kotin websocket adapter for phoenix. subscriptions don't currently work because it assumes a different transport later format and phoenix is indesputably the best websocket technology I've worked with. would love to run apollo subscriptions over phoenix channels. it works great on the browser currently
We’ve actually just released a roadmap for rehauling the Apollo Server transport layer, to better support different technologies there. Does https://github.com/apollographql/apollo-server/3184 help you at all?
Or, if this is something that would be implemented client-side, drop an issue over on the iOS client repo, we’ve just brought on a new engineer dedicated to the mobile native clients, she might be able to better help you.
During my internship we've built a large prototype using React+Typescript, and here are some of my key take aways from it:
- Quite often when using unreleased APIs, you turn to using "any" all over the place.
- Development time is slower than writing in regular JS/React. This started to become a major issue due to the nature of our project being a prototype (fast iterations on ideas and features).
- Lots of frustration when a package doesn't have types (although most major ones do have them).
Otherwise it has been a joy writing the application, and it does "document" your components significantly better.
I think the threshold of when Typescript starts being helpful is reached very quickly. Because there's always some schema in code, yes you can assign any value in JS, but then you have to remember it and account it in other code etc. There's always some schema, you just have to keep it in your head. And with TS I can offload it to the code/IDE to help me. I want to make decision about shape of an object in the moment I'm creating that abstraction or when I'm looking specifically at it deciding if it needs to be changed. I don't want to be forced to remember all those decisions all the time. If some prototype code or script is couple of screens long, sure you can easily fit it in your head and maybe you don't need additional assistance, but when it grows larger, pretty quickly it's very nice to separate process of thinking over shape of objects and process of using them.
Development time isn't slower when you factor in all the bugs it saves you from dealing with down the road when you're either wonder what is the type of an object or why something isn't working during runtime that a compiler could have caught.
Also, if you're working with an untyped APIs, define the types you're using. It doesn't have to be complete or perfect. But having that formal contract will save you time and make explicit your assumptions. In the best case, you can contribute those types to the API to everyone's benefit.
I’d recommend using ‘unknown’ over ‘any’, it’s truer to the code you’d probably end up writing if it was vanilla JS; fairly defensive stuff.
On top of that don’t be hesitant to define ad hoc interfaces on your side of things; ultimately ‘any’ communicates zero information and provides zero defence.
The more I start to use other techniques the more I feel that ‘any’ really should be the last escape hatch deployed.
IMHO TypeScript saves a lot of time as soon as any project grows over few thousands lines of code.
I'm working on a large project in which both backend and frontend are in JavaScript (node+PWA) and without TS it would have been close to impossible to proceed at the speed we did. Thanks to TS we easily know what types must be passed between client and server, and we can easily refactor or edit code without worrying that some mis-type somewhere will brake things.
We don't use any anywhere (literally), it's not trivial, but as soon as you get to know TS well enough it's absolutely feasible, at least since TS 3.x.
The way we've been using typescript is that when you're first implementing the API using 'any' is just fine, but it's not ready for release until all the 'any's are removed.
What I've found is that there tends to be a happy medium between making everything 'any' at the start and never using 'any' at all that roughly corresponds to how defined our implementation is. When we're designing the implementation as we go, there tends to be lots of 'any', but when we spent time defining the interfaces, there's not as much need for using 'any', because instead there's a specific type. The type itself usually doesn't remain static, but where it's used does.
So for example, when we're adding a rest endpoint, when we know the required and optional arguments/response, we can make a type and validator function and then there's not really a need for 'any' after the validation function, but if we don't know what the arguments/response will be (or the design is still at the 'make every argument optional' stage), then any sort of prototype will be littered with 'any' or '{[key: string]: any}' types.
I'm curious, and I'm prefacing this up front because I'm not always good at writing what I say in a way that may not feel like I'm coming from a good place, so here goes:
Whats your testing story? This to me seems like not writing good, solid, abstracted tests before doing proper implementations of your code. This could be solved with good interface design, and perhaps be faster.
I apologize in advance if this sounds harsh. This sounds like the exact thing folks on my team were trying to do, and it was turning things very sub-optimal.
(Disclaimer: I'm a bit of a TDD/BDD idealogue. Not as hardcore as Uncle Bob[0], certainly, but close enough. I think writing Interfaces before Tests is acceptable, I think that might be where things differ, i guess).
At least for interfaces between different modules/layers of abstraction, we use BDD almost exclusively. Our docs tend to be very well defined, so 'any' isn't actually used that often. We focus on end-to-end testing over unit and integration tests, which for the REST API backend means only looking at the request, the response, and the side-effects (especially db writes). For us the docs/spec comes first and then tests, implementation, and consumer use can (and does) happen in parallel.
The REST API is defined using OpenAPI v3, and we use express-openAPI to generate request and response validator functions for every endpoint. Each endpoint needs a happy-path test for 1) every optional argument supplied and 2) none of the optional arguments supplied (if there's no optional arguments then this devolves into a single test), and all side-effects must be verified. The main place where we use 'any' or {[key: string]: any} and then just cast to what's expected tends to be the responses from the database, because the response validation code will catch any actual mismatches (the most common mismatch is forgetting to parseInt and trying to send back something like '1' instead of 1, but sometimes there's issues with the db field being nullable when it shouldn't or not nullable when it should be nullable).
Here's the latest test run on master (hope the formatting works):
Branches are especially low, because we don't test most unhappy paths since we use a middleware error handler with generic error messages for different types of errors that's got something like >90% coverage instead of handling errors within each endpoint. The unhappy paths we do test thoroughly are things like our user-defined typeguards, our middleware error handler, our security handlers, and anything else where we validate unsafe/unknown inputs.
We try to stay away from stringent TDD/BDD unit testing, because 1) the reward from the work required to get there doesn't justify the cost of getting there for us right now and 2) strict BDD/TDD unit testing makes it much harder and slower to try different implementations/refactor things, since every time you want to modify one-off helper functions you need to add a bunch of tests first. We found that cost is worth it at module boundaries (eg endpoints, auth, database), but not for most functions that are only ever called inside their module.
>- Quite often when using unreleased APIs, you turn to using "any" all over the place.
What unreleased APIs are you needing to warrant this? We've used any a couple times, but usually just as a placeholder until the data model is locked down.
>- Development time is slower than writing in regular JS/React. This started to become a major issue due to the nature of our project being a prototype (fast iterations on ideas and features).
Again, I can't say I've had this experience. Development time is _initially_ a tiny bit slower, but once you've setup types, the time saved from fixing type related issues adds up very very fast. Also, autocomplete / autoimporting has actually sped up my development time hugely. Not having to worry about figuring out relative paths or imports and just being able to type a component to import it is magic.
>- Lots of frustration when a package doesn't have types (although most major ones do have them).
This is true, but I've found that 95% of the packages we use do have types. The few that don't, tend to be very small indie packages that don't do a lot, so the lack of types isn't a huge issue.
Interesting. There are downsides to using TypeScript (build chain complexity is the major one for me, although that's getting less and less relevant as more and more tools gain native TypeScript support), but the three you mention are not relevant to me.
- I don't know what unreleased API's you're referring to, but I generally haven't seen the need to use them - if they're unreleased, I try to avoid them.
- Especially for projects with fast iterations, TypeScript has been massively useful. Changing the API around, which I do often at the start of a project, is just so much easier when you've got TypeScript to make most of the required changes, or to tell you where you have to make changes.
- Type availability might be a problem, but I also generally stick to major packages for which it's not. But yes, I have learned to contribute to DefinitelyTyped - which luckily is a relatively smooth process.
Beyond a certain size and complexity (which is not that much), I find that the argument of typescript (and other typed languages) being less productive than untyped dynamic ones, is not true when you look at it as a whole. It might feel slower, especially to begin with, but once you get used to the language and semantics you save a vast amount of time, by the bugs you _don't_ debug and by not having to jump through the code all the time to find out what that function or module was called or what parameters it accepted. This of course is less true if you're using a lot of untyped packages, but as you said, most do have types either natively or in the DefinitelyTyped project. For most modules it's also feasible to declare the module typings manually, even doing it gradually for the parts that you happen to need at a given time.
React is more functional favored. It's very recommended for functional languages having static type systems, at least for language like JavaScript heavily relies on object literal.
The problem is, dynamic OOP languages like Ruby and Python are Okay to work with, since you know the class of an object you know a lot of things (schema, behavior, etc).
But for JavaScript and React, mutable classes are not quite useful since they mutate themselves, and could stop the app from re-rendering. it's more likely people are using object literals, which can hardly go far.
With TypeScript's structural and gradual type system, it's flexible and easy enough to type object literals with the union and intersection types, without forcing people to use classes.
The creator of it answers “why typescript” in a video[1] with a hilarious answer which includes his observation that large javascript codebases become read only :-D
I’m giving a talk on typescript Friday. Some good stuff to understand is index signatures for object lookups, union types, intersection types, combining index signatures with named properties, and compile time immutability with readonly, Readonly<T>, ReadonlyArray<T>, ReadonlyMap<T>.
The language is so much fun to both write and read. There’s a lot of depth to it as well. Excited to get to use it.
This is BS. I've built very large JS projects with hundreds of thousands of lines and never had this problem.
If your architecture is well designed and modular then refactorings are easy and localized to just a small number of files.
On the other hand, TypeScript encourages spaghetti code which makes refactorings span more files; complex active instances end up getting passed around all over the place and makes your code brittle and fully dependent on TypeScript to make any changes.
TypeScript allows you to write a lot of spaghetti code and allows you to delay having to think about architecture until your code is a total complete unmaintainable mess.
With JS, you will discover if you architecture is a mess a lot sooner and you will learn more and adapt faster.
Thanks. I feel like there should be a lot less discussion about tools and a lot more discussion about architecture. I find that with good architecture, the language doesn't really matter at all.
I've built high quality very complex projects in both JavaScript and TypeScript (both alone and as a team lead). My point is that I (and people of my skill level) can complete the project/subproject much faster with JavaScript so it gives me a lot more extra time to write tests.
I see the same response everywhere on TypeScript: when a project becomes big enough, it's a good way to keep it under control.
And I tend to agree: types can be annoying, but when stability and robustness come into play, TypeScript is most certainly the way to go. It enforces good behavior.
And yeah, of course it slows JavaScript down, that's entirely the point..! JS allows you to do whatever you want, but that doesn't mean it's always the right choice
Elm is lovely, and I'd definitely encourage people to take a look at it from an education point of view at the least. However, I think that's a disingenous comparison.
TypeScript is a superset of JavaScript, which means any JS dev already knows most of TypeScript. They're effectively just learning the type system and best practices on implementing it.
The syntax of Elm is extremely different, which would effectively require developers to learn an entirely new language. It's also a completely different proposition. It's a whole framework, which means complete buy-in on a project. I know there are some interop packages, but you would normally use it instead of React/Vue/Angular etc. This means it's only suitable for new projects.
It's a very nice framework (imo) but it's not a true comparison.
There are not many stories of integrating Elm to existing projects, but there are some. We at Conta AS integrated Elm in our big AngularJS application. So Elm work side by side with AngularJS. It’s possible to embed Elm program to AngularJS code, and it’s possible to embed AngularJS component to Elm program. Which makes the communication part easy. It’s true for any other technology such as React, Vue, etc..
I've tried Elm and I think it's really great. I've watched a handful of Evan's conference talks and I respect him a lot.
However, when I choose what tech to go deep in with my limited free time, I want to see more than zero mentions of it in the "who is hiring" threads. I don't feel like Elm's traction has gained any in the two years since I first dabbled in it. If I was in a position to pick the tech stack for my work, I'd give Elm serious consideration though.
Because TypeScript is hardly a new programming language. It's like adding JSDoc to your code and running a linter, in terms of amount of effort involved, and the learning curve is just slightly higher than that.
I am honestly stuck between two worlds, React and Elm. There is a toy project that I've alternately developed using both React and Elm, which is a little neurotic when I think about it. I find working with components to be more intuitive and organized than my Main.elm ends up being, but, even using Typescript's strictest settings I seem to get runtime errors, which bothers me a great deal.
Before trying Elm I'd never used a purely functional language. It's definitely a different way of thinking and I'm glad I took the time to wrap my head around it.
JavaScript interop I suppose. TypeScript allows incremental integration with other JS libraries, while Elm forces an awkward ports/messaging abstraction.
Looked at Elm and it looks quite interesting. however concerns for going to it
- Skill transfer. If I learn react, I can use my JavaScript skill to understand how everything works under the hood. If I learn Typescript with React, knowing react, I can focus on learning new language feature while not learning at the same time how to build the application.
With Elm, I have to learn the language and the framework, all at once, basically learning from scratch.
- compatibilities with libraries. Here I definitely might be wrong, not knowing Elm enough, but okay, say you don't need a framework because it's included. What about utility library. Like i don't know, analytics or fancy animations?
- Job Market. I'm not going to learn a language that I cannot use anywhere, and if I'm a company, I'm not going to choose a language for which is going to be hard to hire people.
Now Elm sounds real cool and I want to like it. But it doesn't seem very wise to spend time doing that vs learning something like Apollo+Graphql, or Typescript, or Vue, which have much more obvious benefits to my career.
The job market is a chicken and egg type problem. Management would not allow Elm based projects as there are no othe Em programmers on staff. We cant ask for Elm programmig experience in resumes as we dont have any Elm based projects.
I wonder how other languages,like Scala, managed to get traction in the enterprise whereas Elm has not even though it can solve real issues that we face with Javascript development.
Scala got traction in places I've worked partly by making the pitch that the team hires people based on software engineering aptitude, not prior knowledge of specific technologies.
A new hire often has to learn a number of new/different technologies. The language is one of the easiest of those.
I agree it is definitely a chicken and egg type problem (which doesn't negate the fact that it is a problem)
I don't know for Scala, but in general Marketing and money thrown at hackathon and meetups can get things moving in that regard. However I believe Elm doesn't do much of that.
I love Elm, but from a practical place, I think ReasonML is more fruitful to learn because of the fact that any valid ReasonML (sans JSX) is also valid ocaml, so you're nearly learning two languages at once.
Well you are a minority then. I also love elm. I gave talks about it in meetups/confs and organized a local elm meetup but finding job as elm developer is extremely hard.
Similar to the other commenters, I find this comparison to be a bit unfair since TS is not a completely new programming language.
Apart from that, Elm is a great language! I've been dabbling around with it in the past and I especially liked its great DX and that it's purely functional. Comparing it to ReasonML would be very interesting, too :)
Would be true if Typescript significantly differed, but as it is, it’s basically JS with a few type annotations. The only learning curve is typing these dynamic constructs properly, rather than wrapping your teams head around other concepts, constructs and patterns.
I check in and pick Elm up again every few months to see where it's at, I really enjoy using it and agree it's miles ahead of TS in some ways. However I've heard very little from the team about a next release and 0.19 left a few open questions. It's not something I'd pick for a project at work.
The whole discussion of typed vs untyped is stupid. Types and structures are all around us (albeit simple or infinitely complex, eg inductive vs coinductive). It is rather a discussion about typesystems that check type constraints immediately before shipping and/or after shipping while running the program. Without a proper typesystem the programmer has to check the types in his mind, or the end user gets a runtime error.
For small hacks or prototyping type checking is not really required because of low overhead. But in non trivial larger, long running systems with a lot of maintenance the cognitive overhead is too big to not use a typechecker. Even if you decompose in microservices or microfrontends you still stuff like schema definitions and IDLs. Even hardcore ecmascript evangelists use linters extensively before shipping (potatoe/potato linter/typechecker)...
We can compile C to WASM, which gives us effectively most dynamic languages on the browser. Say Python. I have a plan to put a tiny web framework together just having Python doing the front end stuff. Not react or anythng but enough for "most" use cases (I know I know)
But JS just feels like it changes too fast, its been well over a decade of wheel-reinventing when the fundamentals of tabular display, layout and so forth have to be relearnt every year or two.
Meanwhile I think JS is one of the best dynamically programming languages and your post just feels like it's belaboring the same old Python vs Ruby or tabs vs spaces debates.
Frankly I don't find any of the other client application platforms any more compelling than what we have with the web.
JS should be like SQL - everyone's second language and a standard that can be taken from job to job and company to company and still be effective.
But while I this week used my decades old SELECT skills for a quick two day job, I have also been utterly stumped trying to modify react codebases.
JQuery is probably the closest thing to SQL in the JS world and it is fine - but there appear to be few technical reasons not to use it and lots of fashion reasons.
And so while I could just stick to JQuery and some widgets, the weight of development seems to be in the morass of change that is so very hard to stay on top of.
Yes this feels like crotchety old timer moaning, even to me. But there is something there. I am having trouble expressing it however.
Client development was never trivial. Your old iOS/Android skills also expire due to SDK changes. Apple recently switched out the entire language you're using.
I don't think SQL is a great example either. Your next company could be using any database where you aren't even writing SQL. And you're expected to know more than standard SQL to, say, use Postgres. Your rant here to me is like getting mad that "just SQL" isn't enough because you constantly have to learn more at your next job that uses Postgres, DynamoDB, etc or that "just <language>" isn't enough because your next job uses a different framework than you're used to. I don't think your rant is consistent, so it just comes off as confused anger towards JS client development.
Maybe you don't have the stomach for client development where code must run on a machine you don't control? That isn't a disparaging remark either, it's very reasonable to prefer the cozier environment of writing code for machines you do control (like application servers).
I get why TypeScript is quickly becoming the standard, but the problem with it is that it's still JavaScript, and contains all the warts thereof. If at some point your attention turns to the very real benefits of static typing, why not choose an objectively better language?
That's why after ~10 years of being a JS/Node developer, I switched to Dart, not TypeScript.
What you're asking for mostly exists. You don't even need a checkbox.
TypeScript support has been included with create-react-app since v2.1.0, with all the features enabled. VS Code ships with syntax highlighting and command completion for TS.
(WARNING: npx runs stuff from the internet on your machine)
That will make a 'typescript' React app run on your machine. Obviously App.tsx isn't actually doing any TypeScript stuff, but if you add some it will work.
Typescript certainly eliminates a class of errors from a JS codebase and can also make development more productive.
My issues with it are that it's just an extremely verbose language. Just like all Microsoft languages. All the while lacking advanced type system features like algebraic data types, pattern matching etc.
I feel almost the exact opposite: TypeScript is delightfully terse while still achieving its main goal of looking and feeling like JavaScript. I've used languages where things start looking like line noise (hi, Scala) and I very strongly do not get that feeling out of TypeScript.
You have discriminated unions and the compiler is clever about them, so you can implement ADTs if you want them with a little but not a nasty level of boilerplate. (They can also be implemented with an abstract class if that's more your bag.)
It lacks pattern matching because it intentionally doesn't include a runtime component, which I think is also wise. Options exist if you want to use them. I've seen people use Purify to good effect.
I guess it depends on what you compare with. If you're coming from Java or the like, surely TS does not feel particularly verbose.
However, coming from a truly terse language like Haskell you'll just feel TS is too verbose and not very elegant. TS is the most verbose and least elegant of the languages I'm personally using, on par with Dart.
ADTs not only feel dirty because they're not first class citizens (you build them with the TS primitives by adding a discriminating union key) but also somewhat useless since you don't have pattern matching.
Although pattern matching would not be a trivial problem to solve in TS. It could be solved with the compiler, however, you'd still have to hack the compiler API quite a bit too and TS doesn't even support integrating custom extensions with a config file like Babel does.
Btw thanks for pointing me to that ts-purify, it looks good!
I'm sure that, if you're carrying a torch for Haskell, TypeScript can feel very primitive--though I don't enjoy writing Haskell, in my experience it turns into an exercise in navel-gazing and write-only code compared to TypeScript or F#--but on the other hand normal people can write it and be productive and that's kind of important, yeah? I think the TS folks did a great job in making something that I can bring into engineering shops to make average to average-plus developers significantly better and significantly safer.
I'd really disagree with the notion that discriminated unions/ADTs are useless; the compiler is clever and will constrain the type in an `if` or a `switch` (particularly useful with an enum type, too). I use this regularly and it's really effective; my `nestjs-auth` library makes it more or less a requirement and its users seem to really dig it. And if you do it as an abstract class instead of a key, you can use polymorphism to take care of a lot of what would otherwise be pretty clunky--this is how you can make a solid maybe/option type in the vein of Scala.
Hehe yes the Haskell community certainly suffers from the dynamics you just described.
I guess my problem with TS ADTs is just that they're done with the TS primitives whereas they could be true first class citizens. But such a feature may be too hard to swallow for your typical JS developer whose first introduction to types is Typescript.
Btw can you elaborate on that Enum with ADTs? Or did you mean that you use Enums with switch-case? I use it too and find it one of the best bits of TS.
I do mean enums/switch, yeah. Makes it easy to have your code yell at you when all cases aren't dealt with.
The thing about making ADTs first-class citizens means that TypeScript stops being JavaScript, and one of the most valuable parts of TypeScript to me is that I can just look at it and know what the underlying JavaScript is. I kind of equate it to writing C on an old platform--it's going to be munged significantly but at a glance you can have a high degree of confidence that the code coming out the other end is going to be what you expect it to be.
I don't understand the desire for terse code, it makes things harder to read.
I also think Typescript makes the right tradeoffs, yes it may not have a Haskell-level type system, but in return, it has amazing integration with JS code and existing libraries, and no runtime.
As a side note, coming from someone whose previous exposure to type systems was Java and Kotlin, Typescript seems to have a pretty powerful type system. Yes, it may not have some/many Haskell-level features but I question whether those would have been necessary in the first place. At some point a more rigorous type system must stop generating value, otherwise we would all be using coq.
Verbose? Do not agree at all. Adding types to function parameters for example is just a simple ": TYPE" I do not see how that could be much shorter.
Furthermore, where possible (variable assignment, function returns) types are inferred, and do not need to be specified at all, leaving you with mostly plain JS syntax.
React is already typed with props. I see no added use for TypeScript. Yet another list of packages makes maintaining very hard and inconsistent (as types are declared in variant ways). Explicit (and simple) functions as React (e.g. hooks) provides won't need strongly typed code, less readability in my opinion.
If you're building a library / sdk, than Typescript comes in place and can make life easier for devs.
IMHO we're reaching the point where typescript (or similar languages) should be used by default over untyped javascript in professional environments. It's like having tests, which are also not generally considered optional. I've been in CTO type roles and already insist on it when I can. I don't think I'm alone in this and many organizations only do typescript at this point.