>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.
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!"
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.
> I'm always confused when someone calls out a large group of people for not having a consistent opinion.
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.
(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.
And who ever thought refactoring JS could be so easy!
type Foo = {a: number}
const o: Foo = JSON.parse('null')
o.a = 1
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. '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.
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.
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.
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.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.
It’s very helpful for dealing with untrusted/uncontrolled data.
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.
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.
No language actually gets there, but there are some where developers mostly don't even remember there is a cast operation.
```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.
They aren't the ones with zero self-awareness.
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.
This is a good list of how to use common React patterns in Typescript.
function fn(arg?: SomeObject)
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.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) ...
I'm really going to struggle if I have to go back to normal JS now which says something!
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.
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.
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.