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.
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.