Ask HN: Is it worth it to invest in learning TypeScript?

61 points by pandatigox ↗ HN
I don't do much frontend programming so I've been mainly using Javascript. However, more and more libraries are using Typescript which makes me think that I should make the jump. But I don't understand how adding types will suddenly make frontend coding more efficient. I tried researching for pros and cons but nothing came up.

98 comments

[ 3.2 ms ] story [ 218 ms ] thread
If you want a job writing JS in 2021, then yes, you should be proficient with TS. That said, there's not really a whole lot to it, other than pre-defining types for function arguments and objects using a couple extra syntax elements. It's actually less efficient when writing, but it's significantly more efficient when testing and debugging, because issues tend to become glaringly obvious. And many developers will attest that it helps force them to write better initial code.

That said, I stick with JS and I'm perfectly happy doing so.

If you know any modernish OOP language and JS, “learning” TS will take about an afternoon.
Seconded. But with an asterisk - I've found, as I've learned more and written more TypeScript, that I write significantly less object-oriented code overall. I use a lot more interfaces and a lot more types, and I use runtypes to describe domain objects (to combine runtime validation and static typing).

Classes are still useful, of course, as encapsulations of state, but I don't remember the last time I wrote something polymorphic in TypeScript that didn't feel faintly wrong. Coming from a Java/C# background, the "easy path" in TypeScript feels to me like it nudges you more towards compositional architecture (which is a good thing!).

To learn some basics, and get started using it, yes.

Understanding TypeScript in depth is not as easy. It's a language with many features (some of which were not designed to make the language great, but to be able to cover the quirks of JavaScript), and a sophisticated type system.

Sophisticated type systems always take some time to really understand. TypeScript's has various features you don't find in many modernish OOP languages, such as structural typing, control flow based type analysis (like Rust), and reasonably sophisticated generics.

I use Haskell professionally for almost 10 years (including teaching it as consultant and contributing to GHC), and am quite familiar with many OOP languages, so I guess I have a reasonable background for "learning" TypeScript. I use it regularly, but I'm certainly still "learning" it for a while now.

TypeScript's error messages can be huge, and tougher to understand than nominal-typing systems, as you need to get used to diving into the structures of types instead of being told that type Dog isn't type Camel.

Another challenge is that there are multiple ways to achieve a type-related goal in TypeScript, and it isn't always obvious which one you should go for. One such example that a friend brought up yesterday is when to use unions vs enums [1]. As you can probably tell from that post, even diving into this topic alone can take an afternoon, and the answer will also change over time as TypeScript improves and adds features. To me, having properly "learned" a language implies knowing those things, as your decisions will make it into the APIs that you and others will use longer-time.

So my point is that TypeScript is far from trivial.

But it doesn't have to! Spending a couple months to learn something well is a great investment if it makes you more productive for years.

TypeScript is a great improvement over JavaScript, and I'd recommend anyone using JavaScript to learn and use it.

[1]: https://stackoverflow.com/questions/40275832/typescript-has-...

Typescript isn’t to make your code run faster- it’s to make it faster to write your code, especially in a team environment. It’s super nice to know what types that function your coworker wrote will accept, or what is in that struct. It’s really just a tool to boost programmer confidence, and a must-have for any large new project IMO.
Just to nitpick: it is to boost team programming efficiency, not confidence. TypeScript acts as a tool to force programmers to better communicate the intent of the code, so that other programmers do not guess the intent incorrectly and write bugs. Especially when there a change to old code that may cause regression. Because the compiler catches these situations, regression does not happen and programmers save a lot of time by avoiding unnecessary bug hunting trips.
Fair enough- when I say “confidence” above I mean “I’m confident this function is the correct one and has the parameters I think it does”
(comment deleted)
Typescript doesn't necessarily make your code any more stable than equivalent vanilla JavaScript that validates function arguments properly. However, it does make the code more terse and the types more apparent. It adds some build time complexity and also requires (depending on how you configure it) that you define types for every little thing (e.g. argument objects passed into functions). In IDEs, you tend to get better autocomplete on Typescript than JavaScript. That said, I still prefer JavaScript.
(comment deleted)
> Typescript doesn't necessarily make your code any more stable than equivalent vanilla JavaScript that validates function arguments properly

You're confusing runtime checks with compile time checks. If you mess types while developing, you will know instantly with TypeScript but you'll find out during testing with JavaScript (or in production)

Additionally, TS allows the team to be more confident in refactoring, or updating dependency versions, where the interfaces and argument types may change (because obviously, semver is not ubiquitous, and also not fool proof).

The incompatible changes are more likely caught at compile time, and far less likely to become apparent only when deployed.

That said, also TS itself is not fool proof - there are times where the config in use (strict vs. non strict null checks) and other factors (e.g. de-serialising JSON) can mean that your data does not in fact match the expected types.

> Typescript doesn't necessarily make your code any more stable than equivalent vanilla JavaScript that validates function arguments properly

I don't agree. 99% of javascript/typescript programs will be dealing with data that is potentially null and without strict compile time type checking that typescript affords writing correct javascript programs is almost impossible.

> without strict compile time type checking that typescript affords writing correct javascript programs is almost impossible.

It's tedious but it's far from impossible.

Without an automated system telling you where to add checks for possible undefined values I think it's actually impossible in practice.
I think it is very possible. There's a simple technique for that: check everything, all the time. As I said, it's tedious but it's far from impossible. You can probably optimise it by checking less things in practice.
> vanilla JavaScript that validates function arguments properly

Validating perfectly at run time is exceedingly difficult and tedious. Does anyone actually write defensive code like this?:

    function sum(arr) {
      if (!Array.isArray(arr))
        throw "Invalid type";
      let result = 0;
      for (const val of arr) {
        if (typeof val != "number")
          throw "Invalid type";
        result += val;
      }
      return result;
    }
By contrast, the TypeScript code is reasonable:

    function sum(arr: Array<number>) {
      let result: number = 0;
      for (const val of arr)
        result += val;
      return result;
    }
> it does make the code more terse

No, TypeScript code with explicit type annotations is less terse than the typical unsafe JavaScript code that lacks type checks.

> It requires that you define types for every little thing

No, the TypeScript compiler has type inference. It can assign types based on how values are passed between known functions. While it's a good idea to explicitly type all function signatures in TypeScript, it's not mandatory unlike Rust.

The thing is, though, that Typescript is not solving runtime type checks. Typescript is declarative, it does not check at runtime and if you have any code interacting with third-party data sources and you do not check there, things will explode down the line.
I half agree with your response. It's true that third-party data sources need to be validated using runtime type checks, whether in explicit custom code or through some schema validator framework. But TypeScript is very helpful in the common case where the caller is fully typed, so the callee doesn't need to do all the needless defensive checking.
You would have to check your third party data sources anyway? I fail to see how this is a knock against typescript.

Setting the type of unknown data to `unknown` forces you to validate it.

Oddly enough, Typescript would be more useful if the type checks transpire to explicit defensive code. Otherwise, it’s just, eh.
Think of TypeScript as writing JavaScript with inline tests: It takes longer but it produces "safer" code (with fewer bugs)

Is it worth learning it? I'm sure type-less job offers will continue to exists in large numbers, so if you don't like it then don't study it.

Note: TS is generally not referred to as "testing", this is just my simplification. I've been using TS for years for everything and it has avoided a lot of bugs — which is something I notice when porting projects to TS.

(comment deleted)
Depends, yes, no. It's mostly for people using VS Code to have auto-completion and errors on null values
Over the last couple years I've worked at a F500 company with no TS and a startup which auto generated TS types (so i.e. 100% TS) and guess which company moved faster? The F500 you literally had to get on multiple meetings across multiple teams just to figure out what values something might be. It took months to onboard new engineers and the bus factor was insanely high.

Once you've worked in a codebase with consistent TS usage, it's very, very painful to go back to vanilla JS, so imo yes, TS is worth the investment.

100% yes. As someone who did JavaScript only for a while, once you make the switch, you’ll never want to go back.

The things you’ll get: 1. It’s going to force you to think more about how you structure your program. You’ll probably end up with a cleaner design. 2. It’s going to make you code faster because of the awesome autocomplete functionality you get out of the box (at least with VSCode). 3. It’s going to give you much more confidence that your code won’t break at runtime. There’s still a chance it will (write unit tests), but it’ll be much less frequent. 4. Types are a kind of documentation, so your future self will thank you.

Yes, absolutely, 100%.

I wrote a post a couple years ago describing my journey learning and using TS, with a set of takeaways at the end:

https://blog.isquaredsoftware.com/2019/11/blogged-answers-le...

I said then that "I refuse to write any more app code that isn't TS", and that's even more true today.

TS is not perfect. It does add overhead. But, it adds a huge amount of value and safety to your app. The goal is to use it pragmatically, such that Value > Overhead.

*edit*

I'll add a bit of additional context here based on my experiences since then.

Typing application code is usually relatively straightforward. I'll use React and Redux as an example, because that's A) what I use at work, and B) I maintain Redux.

Per our Redux TS Quick Start page [0], you'd start with a few lines to infer the TS types of `RootState` and `AppDispatch` from the store, and create pre-typed React-Redux hooks (which are _much_ easier to type than the legacy `connect` API) to save from repeating those types in components. For each slice reducer, you'd add a TS type for the slice's state, and declare the type of each action's contents as `action: PayloadAction<string>`, etc. For React components, add a type declaration for the props of each component, and add types to function arguments as necessary (change handlers, etc). The React TypeScript CheatSheet [1] is a fantastic resource for learning how to use TS + React together. Also be sure to type any data coming into the system, such as API responses, and any other "business logic" you might have.

Those should get you 80%-ish types coverage without too much effort, and all you really need to know is how to declare types for objects+primitives, and a few bits from the React and Redux lib types.

I also strongly disagree with many of the "recommended" TS-related linting rules, like "always declare a return type for every function". It's not _wrong_ to have those, but in my experience TS works best when you can let the compiler infer as much as possible.

I still firmly believe that for _app_ development, it's not worth spending hours and hours arguing with the compiler to come up with a "perfect 100% correct" type definition for more complicated scenarios. If you know that a function takes `TypeA` as an input and returns a `TypeB`, but there's really complex transformations inside and it's really hard to convince the compiler what you're doing is correct, feel free to do whatever `as` casting or `// @ts-ignore` you need to in the middle.

Library types, on the other hand, are the opposite case. Library APIs are often highly generic in both the "use case" and "TS types" senses. There, it _is_ worth spending the time to come up with very complex typedefs with tons of generics and conditional types, because those are often needed to make the _end user experience_ a lot easier. Try looking at the types for Redux Toolkit, React-Redux, or Reselect [2] [3] [4] - our types are complex, so _your_ code doesn't have to be.

I spent a ton of time just yesterday trying to make improvements to the Reselect types, because as a maintainer I want our users to have a good experience. But for you as an app dev, it should be a lot simpler. The payoff is that you get all the nice benefits of TS: static detection of common errors like typos and mismatched fields, documentation of your data structures, intellisense, confidence in refactoring, less need to write repetitive "is this argument the right type?" logic, and much more.

[0] https://redux.js.org/tutorials/typescript-quick-start

[1] ...

I'm interested to hear folks prediction on the future of the language.

As a greyhair I've seen many languages come and go. I don't want to sound pessimistic but the idea of trading one set of problems for another isn't novel.

I am of the opinion that all roads ultimately lead back to the fundamentals.

If we're wild-assed guessing, mine is that TypeScript eventually merges with JavaScript. It is a wisely designed set of optional type constraints that aren't incompatible with JS proper. Seeing as how every conformant TypeScript compiler is also a conformant JavaScript compilers, the split is pretty small and eating one's vegetables is good for you.

(With one nit: TypeScript enums are weird and while they work in JavaScript ways they are weird and might be best off being rethought, even if it's backwards-incompatible.)

If TypeScript becomes natively interpreted (instead of transpiled), I might actually consider using it for my personal projects. In the meantime, I don't use it because I cannot stand the build, bundling step and the problematic debugging experience with source mapping (and difficulty to debug production errors where source maps are not available). If you write code correctly with loose coupling and good tests, static typing only provides marginal benefits.
Your last sentence is writing a very large check that I'm not confident at all you can cash.

Static typing obviates most runtime type checking, which your module boundaries need to be doing (and exercising in tests). Libraries like runtypes deal with the last mile of it by generating runtime type guards that also project back into the static type system. This removes the need for entire classes of tests, as well as many of those handling intra-module transforms. It's telling that the people doing some of the best work in, say, Ruby are in on both tooling like Sorbet and libraries like dry-types/dry-struct -- because the benefits are significant in terms of minimizing engineering risk.

Static typing is also standardized and machine-readable documentation. This can't be understated. Human-written documentation rots with changes. The guarantees at module level from your static types does not.

Particularly when gradually introduced, these are significant force multipliers. If you want to take on the additional risk for your toy projects, more power to you (I sometimes write Ruby when doing something that doesn't matter) but I certainly find those benefits significantly more than "marginal" if anyone else ever has to deal with my code--including future-me.

>> Static typing obviates most runtime type checking, which your module boundaries need to be doing

I've heard this argument many times but it's misleading. Well written functional tests will be able to identify type mismatches implicitly. For example if you add 10 + "1", you will get "101" - The test will fail. You don't need to check if the result is a string, you just need the assertion to expect the number 11. Tests will catch type mismatches implicitly. In practice, this applies to pretty much every kind of type mismatch. Type mismatches break functionality; so if the functionality is well tested, then type mismatches will be caught implicitly.

Thats weird, the best ppl in JS seem to be tearing TS out due to compile time/toolchain perf problems.

Do you have any large typescript projects?

I personally hate everything about the transpilation step; the time it takes (slows down dev-test iteration by a lot), the source mapping aspect, debugging in a live production environment (I hate having to debug transpiled/mangled JavaScript). I hate having to handle multiple permutations of engine versions (too many possible permutations of tsc and node.js version numbers which introduces configuration and setup difficulties; it rarely works out of the box; it's a nightmare for open source projects when you're potentially dealing with a broad range of different operating systems and environments which you have 0 control over; maybe the user needs tsc version x.x.x for some different reason and you cannot force version y.y.y onto them!!! So their tsc version will not work with your tsconfig)... It just adds a ton of bloat, dependencies (security/maintenance burden), setup/compatibility problems and the value it adds is marginal.
(comment deleted)
TypeScript and JavaScript are the same language, and JS is definitely not going anywhere.
I think Typescript is here to stay, way more than Coffeescript was. On the other hand, I wonder if there's not a missed opportunity by compiling TS to JS. You lose all the type information that could be used to optimize the code.
How so? Coffeescript was mostly absorbed by JS, a true success story. The cutting edge JS ppl are moving away from TS right now, at least some of the ones I follow on twitter seem to be over it. OP might be on to something, not wanting to waste time chasing trend n-1
> The cutting edge JS ppl are moving away from TS right now, at least some of the ones I follow on twitter seem to be over it.

What do you see them moving to?

This is just a Deno-specific internal thing due to tsc being too slow for their specific case. Deno itself can read and execute TS code and they aren't planning on dropping that.
Right, they just wouldn’t use it themselves.
In the original design doc: https://docs.google.com/document/d/1_WvwHl7BXUPmoiSeD8G83JmS...

> Update June 10 2020: I saw that this design doc was being discussed more widely. Most people don't have the context to understand this narrow technical document - it is only applicable to a very particular, very technical situation in the internals of Deno. This is not at all a reflection on the usefulness of TypeScript in general. It's not a discussion about any publicly visible interface in Deno. Deno, of course, will support TypeScript forever. A website or server written in TypeScript is a very very different type of program than Deno - maybe much more so than novice programmers can appreciate - little of Deno is written in TypeScript. The target audience is the 5 to 10 people who work on this particular internal system. Please don't draw any broader conclusions.

Intertia. TS is way more popular than Coffeescript ever was, so there will be more code to maintain, and the network effect applies here.

> The cutting edge JS ppl are moving away from TS right now, at least some of the ones I follow on twitter seem to be over it.

Who are these people, why are they moving away from it?

> OP might be on to something, not wanting to waste time chasing trend n-1

If you need static typing for JS now, TS is your best bet. You can also wait N years for TC-39 to add it but so far nothing has been proposed.

It seems to me that there is a divide of angular typescript apps that are about 3-5 years old and a new wave of react flow apps. Wonder what the stats are.
That might be just my bubble, but I've never heard of anyone using Flow, especially with React, whereas I often hear about React with TypeScript.
TypeScript is the biggest factor that made JavaScript coding bearable to me. Before that, I was collapsing under the weight of my ~1000-line JavaScript programs, because I couldn't mentally keep track of types and names. Similarly, revisiting my old JavaScript programs after a year, it was tedious to reread my code to infer the types and structures.

I know the rules of JavaScript, including all the weird type coercion stuff, undefined, etc. I know how to write correct programs, but I hate the human effort of having to jump around my codebase to recheck definitions and to double- and triple-check my work for silly mistakes.

Writing TypeScript code makes me do a bit more work upfront to annotate types. But in exchange, the compiler helps me tremendously in tediously checking all the types and names. Functions with types are more self-documenting than untyped functions; it's easier to read and figure out what a function does at a glance.

In a sense, compile-time type-checking is more powerful than unit tests because unit tests only cover functions and branches and (run-time) types that are actively taken, whereas type checking applies to all code all the time.

Before discovering TypeScript, I used this workaround a few times: For complicated pure algorithm / data structure stuff (not DOM or user interface stuff), I would prototype the program in Java with all the glorious types, test the heck out of it, and then translate it line by line to JavaScript.

Yes. It's a tool that helps you. It has its quirks, just like any language, but they are outweighed by the benefits.

I was hesitant at first too, but I am sold on it now. Most libraries provide types too, so the entire JS ecosystem has basically adopted it as well.

I don't like it.
This comment might actually have been helpful if accompanied by a list of things you don't like about it.
Keep in mind TypeScript is just types, an added compile step, and branding.

The ECMAScript spec will probably supersede TypeScript in the near future anyway. This has happened historically with other dead Javascript dialects (example: when classes were included in ES6).

This will only happen if ECMAScript adopts types, which does not seem to be planned in the near future, afaik. Otherwise I can't see how it would supersede TypeScript, because that's the core reason to use it.
They could add a new directive ala use strict; "use types", that allows one to write types. In the beginning they could be ignored (ala Python's types), but then maybe use them to optimize the jited code, and later catch errors?
(comment deleted)
Some modern features such as class syntax, arrow functions were championed by coffeescript, and later adapted by TC39. It wouldn't be too far fetched to think that when TC39 will start considering adopting types, they will look similar to what we use nowadays in TypeScript.
If there is an ECMAScript type implementation some day in the future, someone will surely write a transpiler to turn typescript into the new standard.
Static typing reports certain classes of error in your program at compile (aka "transpile") time. This reduces the number of tests you need to write and gives you more confidence about the correctness of your code than is possible with dynamic typing.

The alternative is finding out some time later (possibly in production) that your function that had always been assuming the arguments were non-null strings suddenly generates an error when it gets passed in null or a number or object or similar.

TypeScript is a huge productivity boost for JavaScript programmers (and similarly mypy for Python). There's no way I'd be happy working with either language without these tools. TypeScript is also useful on the backend, if you're using Node.

But the node core devs have moved on to deno. They also recently ripped out their TS code. Skate to where the puck will be.
I just recently finished my job search and a *lot* of the startups I interviewed at were using typescript. So if you want to do frontend work at unicorn startups I suggest you get experience with that.
I've found that the only real downside to typescript is that you are adding yet another layer on your at times unstable JavaScript environment.
Just to add an aspect that I haven't seen so far.

TypeScript makes your interfaces and their documentation much more explicit than with vanilla JS. If you work on your code in a team, you actually want to very clearly document the inputs and outputs of your functions and the types of your data structures. Doing this with JSDocs is cumbersome and error prone in my experience and actually more work than just using TypeScript.

Having explicit types with TypeScript helps both machines (better / safer refactoring) and the humans to reason about the code.

So the bit of extra effort to add the types is very well invested. And keep in mind that developers spend much more time reading code than writing it.

You use TypeScript with intellisense to tell you the types and shapes of variables/objects. It prevents you from accidentally, say, assigning a number to a variable that was previously a string. It prevents you from assigning a prop to an object in the wrong place.

That's all it is, and it works.

The "pros" of Typescript are basically the same as using a typed language.

* If you don't know a typed language - learn one. TS is probably the easiest place to start.

* If you know a typed language, TS is just going to be a different flavor of that.

If you use JS to sprinkle in some jQuery style interactivity on an otherwise static website, then TypeScript is not worth it because of the additional overhead. For anything more complex and most single page applications, yes do use TS.

TS offers a better developer experience (better code completion in your IDE), types help document your code and refactoring will be less painful. Also you gain some level of type safety (though less than languages with a sound type system).

Drawbacks are slow compile times and cryptic error messages and general having a additional tool that adds complexity. Also the level of type safety is dependent on how disciplined you are (though some linting rules might help).

There are languages with much better type systems and faster compile times that transpile to JS that might also be worth noting like Elm, ReScript, Purescript and so on but TS has of course the best migration story with the "just JS but with types" promise.

So generally, yes learn it, use it.

From my experience it is an absolute no-brainer. If you have used any typed language before I would say you can get productive in about an hour.
Others will talk about the benefits, so let me give you a few gotchas from us:

1. It does not replace type checks at runtime (in integration layers)

2. Compilation and startup (in backend land) is slow

3. Some tooling seemingly caches (webpack? ts-node?), we regularly run into situations where rebuilding locally works but then building for prod fails with type errors

1. You can deal with this with stuff like io-ts or runtypes. I strongly recommend either, though I use runtypes myself. Defining your objects in runtypes and having it implicitly return types for use elsewhere in your application is awesome, and it even works with branded types.

2. Yes and no. It's not the fastest build out there but if you've enabled incremental builds it's quite quick unless you're touching heavily related-to code (libraries that are imported all over the place). If that's not fast enough for you, I've seen people having very good success with `swc`.

3. I can't speak to webpack, but ts-node shouldn't be caching anything last I looked at it.

2. esbuild has support for TS (it strips the type annotations out during build), and is plenty fast (compared to webpack, rollup etc). So bundling itself should never be an issue. The other aspect is type checking. If you want TS to type check your project, then yes it takes a bit of time. But that's no different than running tests. Of course running those will take some time. Once the project is bundled, the startup time should not be affected at all, it's all JS anyways.
Typescript is for big code bases and big teams.

The smaller your project, the more pointless typescript will feel. Working alone on a small project, it’s a LOT of extra stuff on the screen to avoid a very specific kind of bug.

I disagree. For me Typescript is incredibly useful at any size. I find myself missing it while solving leet code problems.

And it takes 5 minutes to set up.

I’m using it for a collection of small projects -- I appreciate that I can come back into a codebase I’ve not touched for 3 months, and my IDE tells me what the data structures are instead of me having to page-in all that knowledge manually :)
I disagree, for me it saves a lot of time by giving you more IDE tooling that you can lean on and less stuff you have to hold onto in your brain.

Once you're proficient in TS, adding type annotations really doesn't take much time, and it helps save time testing by letting the compiler "test" your code before it even runs.

If it is good for your career, then it is worth the investment. Whether the tech in question makes sense or not is pretty much irrelevant.
The payoff is reduced cognitive load. Essentially you’re offloading a bunch of it to the type system at the cost of type annotations. A simple example:

    const add = (a, b) => a+b
can do quite a few surprising things depending on the types of a and b.

Whereas how this will behave:

    const add = (a: number, b: number) => a+b
is far easier to keep in your head. Multiply that effect by 100k lines and the TypeScript program becomes a lot more fun to build on because you get to spend less time thinking about edge cases.