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] https://react-typescript-cheatsheet.netlify.app/

[2] https://github.com/reduxjs/redux-toolkit/blob/v1.6.2/package...

[3] https://github.com/reduxjs/react-redux/blob/v8.0.0-alpha.1/s...

[4] https://github.com/reduxjs/reselect/blob/v4.1.2/src/index.ts...