Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Etsy’s Journey to TypeScript (codeascraft.com)
173 points by elorant on Nov 16, 2021 | hide | past | favorite | 152 comments


TypeScript is one of those things that doesn't feel like it's doing much when you start working with it but then when you try to switch back to writing vanilla JS you wonder how you ever managed. The editor tooling alone makes it easily worth the setup time.


This may be the perspective of a JS veteran (who has the types of common APIs committed to memory), but I keep a respectful distance from JS and only seriously touched TS yesterday and already the benefits seem significant.


As a JS veteran, it took me a long time to come around to TypeScript. Early on, there was a lot that was missing from it and a lot of libraries weren't really terribly pleasant to use with it, especially if you're using something like angularjs or Ember with its own DSL for templates that isn't TS aware. You really got accustomed to either memorizing the types of things, or remembering how to look them up very quickly.

But, two things have happened. My once sharp-as-a-japenese-knife memory feels a bit more like safety scissors nowadays, and the type system has added such fantastic support for useful features - unions, enums and the lot keep getting better with every release. Generics, mapped types and advanced types get really close to being able to fully describe the full expressive power of JavaScript. I dove in head first a few years back, and have only a few small quibbles over what it can and cannot express, or how easy it is to do so.

With all of that said, it can be really hard to sell JS veterans on TS. Intellisense in webstorm and the like can be quite powerful, especially if they drank the JSDoc kool-aid, so it can be tough to really clearly spell out the advantages since their tooling mimics a type system already.


> With all of that said, it can be really hard to sell JS veterans on TS. Intellisense in webstorm and the like can be quite powerful, especially if they drank the JSDoc kool-aid, so it can be tough to really clearly spell out the advantages since their tooling mimics a type system already.

As someone who is not sold on TS and writes JS every day, what advantages does TS provide over JSDoc? Both are just valid during compile/build/transpile time, so they seem mostly the same, except one is built and pushed by a huge company that has a history of supressing open source, while the other one is built by the community for the community.


My biggest pet peeve is overreliance on documentation. I recall a project with a large team (30-ish FE devs) and I swear I could open any file in the code base and find at least one error, either from bad copy / paste or lack of maintenance.

That simply isn't an option with Typescript- it wont compile if there are internal errors, so your only real concern is ensuring API responses and the like are what you expect them to be.

Additionally, unless the tooling and spec has changed substantially since I last used it, JSDoc is awkward at best at expressing anything other than simple types. You can pretty drastically reduce the menial effort in some cases with advanced, mapped and generic types. For example, if you have some object of type Thing, and you need to represent an object with all of the same fields as being optional (say for a PATCH endpoint), it is simple as using the type `Partial<Thing>`. No worry that the two types will ever get out of sync with each other.

Another example is that you get control flow analysis- more than simply detecting unreachable code from return blocks, there are a few ways the tooling knows that a type is refined. If you have an optional parameter to a function and an if statement that checks it is set, within the if statement TS knows that it exists, but outside will warn you that it might not.


To be honest, I don’t know how deep you can go with just JSDoc. Can JSDoc tell you what the structure of an object must be based on the sequence of control flow and functions the object has gone through? Because that’s my favorite aspect of typescript in the abstract.

I like that you get something like documentation as a computed property of your object based on what it has moved through over time. I like that I can feed an object to a generic function and see what it’s shape is when it comes out, all without actually running the object through the function or looking at the functions documentation. That sort of thing.

I’m guessing from your comment that JSDoc can do things like prevent you from shipping if you try to feed a function an object that is incompatible with the function signature. TS can also stop you if it might be incompatible, which results in creating a provable system.

Edit: you can also use typescript (the compiler) with JSDoc annotations instead of typescript (the syntax). It occurs to me that this might have been what you meant. If so, and you’re writing typescript conforming JSDoc, then I think the only difference is (mostly) just a philosophic one. Do you write ts syntax and this “force” the code to be compiled/checked before it can run (except not really because lots of people are separating compile and build these days), or do you note that it’s going to end up as js anyway and so just write js that you can use with the ts compiler.

If you’re in the second camp, then I guess the benefits would be:

- built type defs that you can share with consumers of your code…perhaps you can build these even with JSDoc?

- early adoption of ECMAScript features, if you’re into that sort of thing, without needing to worry about if it’s supported on all your targets yet (ie, like Babel)


> The editor tooling alone makes it easily worth the setup time.

The editor tooling, and also the tooling tooling. Working with type-checked, autocompleted queries in Prisma and Hasura has been wonderful (yeah, I know C# has had LINQ forever - I also remember how awesome LINQ was when it came out!)


You can also use jsdoc to ask tools like vs code to run js as typescript and complement your variables with typing information.

I used this recently for a project that couldn't use typescript.


Shame typescript doesn't support flow-like mode where types can sit in comments – making whole file valid js. Not everything can be expressed with jsdoc types.


You can have full TypeScript types in JSDoc-type comments: https://www.typescriptlang.org/docs/handbook/jsdoc-supported...


Not really, I want to inline /*: ... */ type annotations with terse ts code _and_ use /*:: ... */ to escape into normal typescript language where I can define arbitrary number of intermediate types and just use typescript as normal typescript, not some stripped down nonsense at limited positions.


You still can have .d.ts file and refer to that in JSDOC


Annotations on exported part only is not the whole picture of typescript.


That's also useful in mixed JS/TS project (e.g. during a migration). I wrote a short guide: https://darekkay.com/blog/javascript-type-comments/


It's better than nothing but not really a good substitute for actual Typescript.


Welcome to the blub paradox: https://wiki.c2.com/?BlubParadox


"This site uses features not available on older iPhones" wtf


That's a perfect analogy for Rust.


As someone who knows Rust besides several other "powerful" languages, I'd say it sounds more like Crystal now. Many of the benefits of Rust while also having a significant higher productivity.

It's a tradeoff: less use cases (garbage collection rules out some stuff), smaller ecosystem (albeit one can use ruby code as-is or with little modifications), concurrency-but-no-official-parallelism. The benefits: drastically higher expressiveness, terseness and big stdlib.

From idea to production, given good knowledge in either tech: Crystal is way faster to ship things. Only after many months/years into the project and multiple teams involved Rust might catch up.... but this is not the scenario PG speaks of, right?

... or maybe I am just an old Ruby fart and Crystal matches the way I think perfectly.


I had exactly the same feeling when I tried to write TypeScript - in theory it does everything a modern typed language does, it should be easy to use, right? After a week of struggling I gave in and made an actual effort to set up Scala.js instead - and it worked first time, the IDE integration was so much better (even though notionally it sounds like TS should be just as good), and it was just such a relief to actually be able to use those few missing pieces (good nominal types, proper sum types that aren't just inclusive unions, ...) again.


Sounds like you had the opposite feeling.


I had the same feeling as the grandparent's description of switching back to vanilla JS, is what I meant.


> The editor tooling alone makes it easily worth the setup time.

Lets be honest- this is probably like 90% of the reason that people make the switch.


We've converted most JS projects I work on to TypeScript now, and it is game-changing. Interesting to read how a much larger company did the transition.

Main takeaways from this article that I have also found are:

- Convert your most imported & core code first - it's much easier to convert files which only have TS imports - so start with those, and work out.

- Generating types for API response data has removed so many runtime errors. We generate types from GraphQL schemas.

While learning TypeScript takes time (but not much really), now I am familiar with it, I would code faster in TS than in JS.


> We generate types from GraphQL schemas.

This also makes it easier to stub out protobuf/thrift later on. It's a win-win on so many levels.


> - Generating types for API response data has removed so many runtime errors. We generate types from GraphQL schemas.

This setup with codegen reading from a gql schema and creating typed react components/hooks that can be immediately imported is the most productive frontend experience I’ve ever come across.


Does TS happen reduce any of the JS (non-type) craziness as well or is it a superset? I'd like a language with one good way to do things that transpiles into JS. Not sure if that is TS or perhaps Dart?


You will stop running in coercion issues, provided you've typed it all out and don't use the any type. Which to me is the bulk of the craziness.

There still isn't a Float type and you can still recreate all the memeable NaN NaN Batman code that never is an issue in the first place.


TypeScript is a superset of JavaScript indeed. You are encouraged to write code in a way that’s compatible with TypeScript’s type system.


> Does TS happen reduce any of the JS (non-type) craziness as well?

What craziness are you referring too?


I have no idea which craziness they were referring to, but one thing that TS doesn't provide (for probably obvious reasons) that could be considered craziness, is an integer type. You're still stuck with the JS number.


Stuff like "===" and "use strict; for non-baffling behavior." Lots more of these kind of things I don't want to think about.

I want fewer broken things to avoid. A subset, not a superset.


For sure. Typescript definitely gets you away from the worst of these, the typing mitigates most of the craziness about the two exact issues that you mention. But nonetheless, when the rubber hits the road, it's still JavaScript that is running, so weirdness is definitely still a thing


TS does take into account things like === and you can get warnings for using them wrongly. But I'm not certain... I just don't find myself thinking about === and use strict.


> one thing that TS doesn't provide ... is an integer type.

This! I'd love an integer type, and I think this would be within the scope of TS: Use a JS number when running, but type check it to integer.

There is JS bigint though.


My only wish is that the compile times were faster. I know typically people will say there's incremental compilation, but I think this problem leaks into many other places. Like vscode suggestions, go to definition, custom build scripts for which you may need to process types etc. I recently had to use eclipse for some java work and it seemed lightning fast in comparison. This is my only (pretty big) complaint about TypeScript, otherwise it's a very useful tool


If you separate compilation from type checking, the compilation stage is instant. If it’s not, spend the hour or so to understand how to rework your tooling so that it’s working properly. It’ll pay off by the end of the same day.


I have to admit i’m baffled by this suggestion. I get that compilation and type checking offer different value propositions, but i’ve now walked into a couple of projects that were happily compiling their typescript with babble only to realize once compiling with tsc that there were rampant type errors.

I find this idea of throwing away safety for compilation speed to be a non-option.


It’s a perfectly fine option because it’s happening at developer time when you don’t care if it’s wrong. You’re IDE is likely providing typing insights anyway so waiting for the totality of the compilation to see the result of reloading your app isn’t necessary. It’s like using a potentially wrong cache that’s alerts your after the fact that the answer was invalid. Rather than waiting, you proceed to examine the results and likely refine your work before the relatively slow type checker validates the universe.

Now if you end up pushing code that hasn’t been type checked then that’s a different story. Might as well run “git yolo” if you’re not going to run type checks, linting or any other sanity tests before indicating you’re own personal sign off of a commit.


This just seems like a really long turn around time for type errors. This pushes global type checking into the space of unit testing in terms of time cost. I'd personally much rather have tsc take a second or two and give me the full picture than just hide my eyes until I run unit tests.


Could you elaborate?

Shouldn't the type checks happen while you write the code?


vscode + typescript seems to only surface errors in the current file. If a change breaks something in another file it isn't apparent (in my experience at least). I added tsc to a pre-commit hook to catch these issues.


Interesting.

I had the impression it would also include imported files.

Thanks! :)


Your CICD should validate types and lint and fifty other things. The developer when running a local compile does not need to but should before pushing.


Think about the various tools we have available to us in concentric rings, each with an increasingly longer period.

In the inner most ring, the most readily available, happening almost instantly, we have tools like syntax checking, maybe even LSP calls, linting. Time since writing the code is on the order of ms to a second.

In the outermost ring, the least available we have CICD, these checks happen rarely, often on a push to a central server. Time since writing the code is on the order of 10s of minutes to potentially hours.

I have a really hard time believing that folks are getting so little value out of types, have so few project wide issues, or are so willing to rewrite portions of their work to push type checks into the outer ring of CICD.


That's not what's happening. They're not pushing type checking to the outer ring. They are pushing cli driven gold standard type checking to an intermediate ring and worst case the outer ring. Their IDE can do 95+% of the type checking anyway and only when it fails do they need to escalate. However that escalation is rare and does not need to be done every single time they wish to compile something for local testing.


Decoupling build and typecheck is definitely a good first step, but poor typecheck performance is still going to eventually become a bottleneck in CI as project sizes increase.

At Brex we were running up against typecheck times of over 15 minutes at one point before we were forced to address it since it was frequently creeping onto the critical path. We ended up hacking together some CI scripts to share the incremental typecheck cache between builds, which made most checks reasonably fast again (usually well under a minute, averaging 10~ seconds), but there are still frequent large spikes (of 5-15 minutes) in typecheck times on tiny, seemingly innocent changesets (with few dependent files as far as we could tell) that can still really take a toll on productivity.

I believe the recommended solution to scale typecheck performance is to break down your app using interdependent project references that serve as atomic units that only get rechecked if they change, but I'm personally not a fan of this approach because most frontend projects don't really derive any intrinsic benefit from this style of organization (as they function perfectly fine as monoliths, and any added indirection only adds unnecessary friction), and project references themselves have a bunch of caveats and introduce a ton more tooling complexity: https://www.typescriptlang.org/docs/handbook/project-referen...

I wish instead of having to break down our apps into project references manually to reap the performance benefits, TypeScript could just treat each file as a "project" onto itself and give us those performance benefits automatically and by default. In the ideal world I should be able to just run `tsc --noEmit` with the list of files changed from git, and have typescript do the minimum possible amount of work to check all the potential files affected by the changed input files by walking up their dependency graphs, without having to structure my project in a certain way.


Is what you’re asking for possible?

JavaScript can walk both directions on a dependency graph. You can import two things and inject the first dependency into the second.

So, unless you want to “structure your project a certain way,” you’re out of luck. If you ARE willing to conform to those patterns then you can get incremental builds.

As it happens, you can get something similar within a file by declaring a type interface, telling the compiler “you don’t need to walk the interior of this function because look see here’s the signature,” but you lose the power and dynamism of implicit types / complex generics by doing this.

I could be wrong about this; it’s been a minute since ts was my daily driver.


It's late here, looks like I did a few wording mistakes. The actual compilation (turning ts/tsx into js) is very fast and not a problem at all, what I meant is the typechecking


Interesting, I'm generally quite sensitive to small delays when typing/seeing autocomplete, but this hasn't been much of an issue for me, even in VSCode. But maybe that's because I only enable extensions on a per-workspace basis.

Regarding build times, have you tried using esbuild (https://esbuild.github.io)? It's ridiculously fast.

vitejs uses it in their dev mode (https://vitejs.dev) if you wanna give it a shot without spending too much time fiddling with configuration.


The creator of SWC [0] is working on a faster compiler alternative in Rust, so there is still hope for a faster compilation speed in the future

[0] https://github.com/swc-project/swc


SWC doesn't do type checking, which means 99% of the work is just stripped type annotations. That's why it's fast.


It will, which is what the grandparent is talking about. https://github.com/swc-project/swc/issues/571


Ah very neat. Didn't know about that! Though I would be very surprised and impressed if that effort actually succeeds.


Hey gang, I'm the author of this post! Very excited to see it show up on HN. I'm currently on Paris time (finally got some vacation in) but I'm happy to answer any questions you all might have about the article, our transition, and even TypeScript in general (although there are plenty of other smart folks that might have better answers than me on that last one).


I saw in https://codeascraft.com/2021/11/04/mobius-adopting-jsx-while... that you're using Redux Toolkit (which I created and maintain). I'm curious if you have any thoughts on using RTK in general and RTK+TS specifically.


Honestly, RTK made migrating our existing Redux code to TS so much easier. I really can't thank you enough. We often had teams adopt RTK (in favor of some internal Redux-based helper methods) before helping them migrate other files. In particular, the createAction `.match` pattern made adding types to our actin really simple. I think the only thing that felt awkward was using the ThunkAPI generic when we needed to access state in, say, `createAsyncThunk`, but even that wasn't all that bad after we got used to the pattern. I'm happy to provide more specific feedback if you'd like (hi@salem.io).


Awesome, glad to hear that's working out well! You can thank Lenz Weber ( @phryneas on Github ) for much of that API design, both the APIs themselves and the TS typing.

Yeah, let's just say that `createAsyncThunk`'s use of TS generics is, uh.... interesting :) The "object field override" generic technique is neat, but also weird at the same time.

Really, just doing `const state = thunkApi.getState() as RootState` is an entirely valid workaround here. Sure, you can argue it's not as "type-safe", but it's got the same net result and it's less clunky.

Just out of curiosity, has anyone looked at our new RTK Query data fetching API for possible usage? The reception for RTKQ has been highly enthusiastic so far, and it's designed to eliminate the need to write _any_ thunks or reducers for data caching scenarios. (Like everything else Redux, you can mix and match whichever works best for a given use case.)

- https://redux-toolkit.js.org/rtk-query/overview

- https://redux.js.org/tutorials/essentials/part-7-rtk-query-b...


Hi there!

First of all - thank you for all the hard work! My team and I are really impressed with what you've built so far.

The company I work for has recently started to adopt RTK and RTKQ. We're working in a legacy React/Redux codebase, and have been evaluating them as core libraries moving forward as we build one of our new features.

One of our other engineers has been leading the charge with learning/applying RTK/RTKQ.. so he has a better feel for it at this point, but I've been following along fairly closely in PRs.. and as the technical lead on our team, I'm responsible for deciding whether or not it's a good fit for us.

As of just a week or so ago, we've pretty much decided to go all in on it.. despite my initial reservations mostly due to worrying that it's just the latest "new kid on the block"

The RTK part I bought into pretty quickly. Having seen a bunch of flavors of Redux code, I especially love the way RTK allows you to write reducers that mutate state directly.. and just really abstracts away a lot of the complexity, providing a means to writing idiomatic Redux code! On the flip side, I am a little worried about how much it hides what Redux really does, as someone who took a while to really understand it myself. It's started to get into Rails territory from a magic standpoint ;)

RTKQ was a bigger leap for me.. my initial impression was that the API seemed a little clunky.. especially the query "builders" part. While I like the way it allows you to inject different API endpoints where they're used, I do fear it will result in some very tight coupling between it and the requests to services. Having said that, the API has grown on me since I first saw it. So maybe it's just a matter of familiarity.

Anyways, there's certainly more feedback I have! And I'd also be the first to admit I'm not totally fluent in all these libraries and concepts.. so not even sure I'm qualified to speak to these things or if this is what you're looking for. But feel free to reach out to me at bcooke@superdraft.io. Would love some feedback in ensuring our team is really using these libraries effectively.

Thanks again for all your hard work!


This is great feedback, thank you!

Appreciate the vote of confidence. To maybe help address your concerns a bit:

I totally agree that RTK itself is a layer of abstraction that does hide _some_ of what you're used to with writing Redux code by hand. In fact, that's the explicit purpose of RTK in the first place :) At the same time, none of the Redux core concepts change. There's still a single store, you're dispatching actions to describe "events that happened", using reducers that produce immutably updated new state values, and using middleware like thunks to run async logic. The goal of RTK is that you're writing less code to do that :) I talked about some of the design history and goals in my "RTK 1.0" announcement post [0]

Probably the biggest common concern is that using Immer for writing immutable updates with mutating syntax is too much "magic". I get that concern, especially because you do _need_ to know that immutability is a critical part of Redux even with RTK. The best answer I have for that is education, which is why I plastered "IMMUTABILITY IS CRITICAL THIS ONLY WORKS BECAUSE OF IMMER" warnings all over our Redux docs tutorials [1], and have a usage guide page specifically on working with Immer [2].

Beyond that, almost all of RTK itself directly maps to Redux usage patterns that existed long before we created RTK [3].

RTK Query is definitely a much larger leap of abstraction. You're no longer thinking in "actions" and "reducers" and "thunks", but rather "endpoints" and "cache lifetimes" and "invalidation". I covered some of this mindset shift in a new RTK Query section of the "Redux Essentials" tutorial [4]. But, even here, RTKQ's React API was directly inspired from other libs like React Query, Apollo, Urql, and SWR. The "API slice / endpoint builder" concept is unique, and I agree those can get a bit lengthy, but there's definitely a lot of useful properties that come out of defining those up front. But, under the hood, it's all still typical Redux logic: dispatching thunks to make requests, a reducer to manage the cache state, plus a custom middleware to manage cache lifetime behavior. Certainly some bits of "clever" code in there (Lenz Weber is a genius!), but no real "magic" per se.

I'd suggest going through those new RTKQ tutorial pages and see if that helps get you more familiar with how it works. For that matter, the "Essentials" and "Fundamentals" tutorials we added last year may also help clarify just what RTK is doing for you as well.

If you'd like to chat more, I and the other Redux maintainers normally hang out in the #redux channel in the Reactiflux Discord [5]. Please come on by and ask questions!

[0] https://blog.isquaredsoftware.com/2019/10/redux-starter-kit-...

[1] https://redux.js.org/tutorials/essentials/part-2-app-structu...

[2] https://redux-toolkit.js.org/usage/immer-reducers

[3] https://redux.js.org/tutorials/fundamentals/part-8-modern-re...

[4] https://redux.js.org/tutorials/essentials/part-7-rtk-query-b...

[5] https://www.reactiflux.com


We've been a bit apprehensive about using `as` in the codebase in general, but I hear you on that instance not being as big of a deal.

Regarding RTK Query, we have used it, a little! I'm going to pass this thread along to the teammate of mine (@sangster) who's been really excited about using RTK Query more — he might have some thoughts to share.


Heya =)

Etsy is still on `react-redux` v5, because it's taking a long time to remove our `react-router-redux` dependency. Once we can move to `react-redux` v7 and can use the RTKQ hooks, I'll be working towards encouraging more RTK Query usage.

I know you _can_ use RTKQ without the hooks, but there's a lot to figure out in terms of establishing best practices and how to hook everything in with code splitting. I absolutely want to get there, because it solves a lot of problems for us.


Nice work on the migration! I worked on the Dropbox coffeescript-to-typescript migration, and any sort of migration of that magnitude is non trivial :)

We started out with the same approach as you guys, by introducing Typescript to the company, making it possible to write code in Typescript, and encouraging teams to use it. We had an OKR to convert all of our Coffeescript to Typescript by the end of the year, though, so eventually we had to go with an automated approach to convert the rest of the files, which you can read about here: https://dropbox.tech/frontend/the-great-coffeescript-to-type...


I read about that migration when we were first starting! That seemed like a really serious amount of work — congrats on seeing it through to the end.


I didn't see it mentioned, so how do you handle generating type-guards/run-time type validation functions?


In general, TypeScript allows us to write code without worrying about run-time validation — if something says it's a string, we can trust it to be a string. However, this assumption falls apart at the edges between our typed and our untyped code. This is why we left in propTypes in our React style guide components, even though they were made redundant by TypeScript's own types.

Can you elaborate a bit more about what sorts of type-guards and run-time validation functions you're thinking about? The only type-guards we somewhat generate are the ones Redux Toolkit makes for all of its actions, but aside from that, we only really use type guards when we're interfacing with a 3rd party API or something.


Sure, so anytime you read JSON from a file or receive it from a network call, somewhere in the stack you're running JSON.parse() on it to turn the JSON string into the javascript object/primitive (or when you pull in data from a db). What happens if that object isn't what you think it should be?

So like, if you've got an endpoint that says it returns

  interface Foo {
    baz: string
    bar: number
  }
What happens if the endpoint actually sends you

  '{"bar":"23"}'
? How do you check to make sure the 'unverified' data is actually what you think it is?

Or if the db schema has a nullable column but the typescript type for some reason isn't nullable/optional? Or worse, the db query isn't pulling the fields it should be (eg the db schema changed but the typescript definition didn't).


> All of the above took a week or two

Wow, that's crazy fast. Going from JS to TS is a dose of hard reality w.r.t. how many bad patterns JS lets slide. They must have had a lot of devs or a few really good ones.

Although I'm surprised they are still using Babel in 2021. I dropped support for it in my FE build flow in 2018 and hit a few corner cases in Europe and China, but that was it.


Yes — @humphrey is correct here. Literally being able to add type annotations (and validate that nothing fishy happened in production) took a very short period of time, but everything else was the hard part.

Also, I'd love to hear what a common replacement to Babel looks like. Although we've seen a few faster alternatives, Babel's been working really well since we adopted it, and we haven't had a ton of incentive to try something less battle-tested in the time since.


No alternative, just the output of TSC with eslint both set to ES2019[0]. But if that's not a good idea, I'd really like to know why. I don't like extra tooling that I don't fully understand just for the heck of it. @WorldMaker has a good point below.

[0] and I just discovered our default boilerplate is using ts set to ES6 and eslint set to ES2019. Facepalm.


We still typecheck and build in different steps due to speed; we can build our JS much faster than TS can with Babel + Webpack. We also have a bunch of old custom features that our build system needs to support, so bolting them onto tsc was a non-starter.

Plus, we have to support some fairly old browsers (we only recently dropped support for IE11), so Babel is certainly doing a lot of transforms on the bundles we ship to older browsers.

Separately, do you mean Terser instead of ESLint? ESLint isn’t really part of our build process.


Terser? Sigh... Add that to the list of 200+ tooling options that my team doesn't have time to investigate.


It’s a minifier. I am not sure if tsc minifies your code for you or not, but if it doesn’t, you’d definitely want that in your build step. It’ll shrink your built files considerably, and it’s a pretty standard, uncomplicated tool.


I think the "week or two" was just referring to updating their tool chain to also include support for TypeScript - not actually converting any code.


If Babel is outdated in 2021, what is the best practice for FE build flow?

I'm thinking about updating my projects but there too many options to choose from..


The number of transforms that Babel is doing with an "evergreen" config ("last 2 browser versions") at this point is effectively miniscule. It's a massive toolchain for what increasing turns out to be a minimal amount of actual work. "Last 2 Browser Versions" is effectively everything through ES2019 at this point which covers almost all of the "modern JS syntax". If you aren't using custom transforms you might not be transforming anything that matters in Babel in 2021. I've seen a bunch of projects with huge Babel pipelines where the only actual transform was Typescript's type removal and at that point, if your codebase is entirely Typescript, Typescript has all the downlevel transforms you need "baked in" (and arguably a little bit cleaner and simpler to Babel's kitchen sink but also still somehow millions of plugins approach) and it's just setting Typescript's compile option to the ES level you are most comfortable with. (In 2021 that may even be as high as { "target": "es2019" } or higher in your tsconfig.json and even there Typescript's not going to even need to downlevel much.) Typescript can also transform TSX/JSX to JS without the need of Babel, if you are using React.

Even ES2015 modules which some people still think is the big reason to keep Babel around: a) has full Browser support if you use type="module", but most people still want to pack their JS because just about no one is assuming HTTP/2 or HTTP/3 yet, and b) Babel has never done module format transforms, that's always been the domain of your packer (webpack, parcel, rollup, snowpack, what have you).

If you are updating your project stack in 2021 right now my personal top recommendation is that I really like the approach of snowpack (https://www.snowpack.dev/): ES2105 modules with <script type="module"> dev experience (which is great), great Typescript support, and a simpler overall config experience than most other options right now. (It uses esbuild under the hood rather than babel for dev and basic transforms/bundling. It can optionally piggy back webpack and parcel for Production bundling that needs more "power".) Especially that <script type='module"> dev experience feels great now (with Hot Module Reloading too) versus waiting for a full bundle even for dev builds.


Tangential but did you evaluate Snowpack and Vite? Both seem to share the same space and I was leaning towards Snowpack, but Vite now seems to have the mindshare. Which as a small dev shop is important when betting on it for 3+ years


I've not tried Vite yet. I've been happy enough with snowpack so far, and they seem so similar in approach I haven't heard a reason to switch other than yes it does seem to be the new PR "hotness".

Looking at Vite's own comparison I don't see a strong reason to switch that would impact most of my projects: https://vitejs.dev/guide/comparisons.html#snowpack

But also I don't use Vue and I don't trust CSS Modules yet [1] which is their other most impactful feature of Vite.

([1] It still seems like reliable Browser support for CSS Modules is still 5 years away at the current rate, if it ever gets accepted as a standard. I don't think there's any obvious current objections and few browsers outside the Chromium hegemony to even really object today, but it's still one of those web standards that smells to me pessimistically like "it's never going to work right cross-browser", and even if I were less pessimistic about it, I prefer not to use them because I think they bloat CSS and subvert the intent behind cascading. At that point I think you might as well just use CSS-in-JS and that doesn't have to wait on web standards that may never arrive.)


I trialed Snowpack earlier this year and found I ran into quite a lot of hard to debug issues. Around the same time I saw that Rich Harris, creator of Svelte and Sveltekit, opted to use Vite over Snowpack for Sveltekit because more things, like CSS and and SASS, just worked out of the box.

I tried out Vite and it was a much smoother transition. As you said it seems to have more mindshare now and I would guess that is because the out of the box experience is better.


This is a fantastic answer. Thanks!


We use the JS outputted by TSC in both webpack and nuxt projects. I honestly don't know if that is bad, but I have such a small team that no one really knows. We're not Etsy-sized. :) This article drove it:

https://blog.logrocket.com/why-you-dont-need-babel/

EDIT: Plus we also wanted to go on a package diet because we found ourselves doing too much cut-and-paste from github w/o really knowing how to use all the tools.


Learning Typescript was probably one of the best investments of time I have ever made. It took like 3 hours to learn all together and saved me at least 20 hours of debugging already.


> It took like 3 hours to learn all together

Me, raising an incredulous eyebrow.


It’s probably better to say 3 hours to learn enough to start coding a project.

If you already know JS I really think you could learn 80% of TypeScript in three hours. But the last 20% would take a long time: generics, union types, that sort of stuff.


Yeah it's pretty intuitive, just type things with :<type> where the any's are inferred (squiggles).

The harder parts are the advanced types which you can learn middle out as you go.


And if you're already familiar with such things, I could definitely see picking it up rather quickly.


Sorry, not to learn entirely but to learn more than enough to benefit from it greatly.


Not if you already know JS and something like Ocaml or Haskell


They already knew JS.


Still. Generics. `extends` with ternaries. `infer` within conditional types. Type narrowing. Lots of the language quirks.


This is correct. I feel there are 2 distinct levels of knowledge with Typescript: the application level and the library level. The application level is fairly easy, but if you want to write libraries with good types you're going to have to dive in the more complex features of Typescript.

Still not rocket science, but it takes way more than 3 hours, even if one studies it intensively.


Typescript… Hated it as first. But once I realized it was for writing JavaScript with a C# flavor, I don’t think we could go back.

The only thing that makes me scratch me head about TS is that you declare your types backwards compared to C#. Ex. value:string vs. string value.

Is there any good reason for why Microsoft did that? Or can that be updated so a C# developer can jump into Typescript and write TS and TSX components with less friction?


Not an authority on the subject, but on the implementation side I guess it simplifies parsing and "type erasure" (I don't know the proper terminology for transpilation).

As for developer experience, I'm sure it helps gradual typing.

I much prefer that style since it doesn't keep pushing the most important names to the right and causing jagged textual structure (never knowing at which column function/method names begin, unless you go with a multiline declaration style, which some C/C++ shops do). At this point I wish all languages with a C origin used name-focused as opposed to type or qualifier-focused style, but you can't really change it now.


Types don't make you write better code. In my opinion TS makes writing bad code easier because intellisense. Every TS project I have worked on is as messy as the JS projects.

I have nothing against type systems. I have years of experience with C, C++, and Java. However, I happen to think functional programming is a lot more "game-changing" and TS makes writing functional code more difficult. But hey... types are enterprise so it must be good.

I would truly appreciate it if our industry would dedicate more time to training both new and senior devs to write better code instead of chasing the every popular/new language/framework/build-tool that promises to solve all the things.


Could you elaborate on your statement that intellisense is associated with bad code? Intellisense is available for just about every language under the sun, so clearly this isn’t a TS specific issue. Even dynamically typed FP langs have pretty solid language servers these days.

You also seem to suggest that FP and types are somehow opposites. What are your thoughts on a strongly typed functional language like Haskell, Idris, or an ML-derivative?

Regarding your thoughts that TS makes FP more difficult, more than, say, C++ or Java? I’ve found that one can write FP style code in just about any language, though some obviously make it easier than others.

I don’t use TS, but I would have thought that its JS roots and then fact that it’s fairly flexible regarding how strictly one can adhere to its type system, would lend itself to an FP style well.


Again, I have nothing against type systems. As you pointed out some languages make FP easier than others. C++ and Java's type system, from which TS's type system descends, are not FP languages. You _can_ write functionally but they do not afford much ease.

Intellisense does make code easier to write, good code and bad code. Yes, in all languages. My concern is that every colleague I have spoken with about TS conflates this ease of writing code with better code. It's not. They are still writing highly coupled, imperative code but now with types.

Garren pointed out in another comment the book "Domain Modeling Made Functional" which I have read and have recommended to several other devs. It provides a great example of using type system effectively. While TS's type system isn't quite the same as F# I think a good portion of this book still applies, both types and composition. JS will be getting a pipeline operator and pattern matching in the near future. I expect TS will be updated to support them. This makes FP more relevant.


Your argument against TS is that it makes writing correct code easier?

There is no situation where better autocomplete and compiler errors make code worse. Coding should be as easy as possible, and footguns should be almost impossible to introduce.

I've been writing code for 20 years, and TS is the first language I used where I would often find 0 runtime errors in my first version of a project. It takes discipline to achieve that, but TS helps with discipline by saving you the overhead of all the other JS nonsense.


I think I also agree that TS has made js programs harder, but not in a bad way.

You see there was a quote back in the day about a discussion between a senior clojure developer and a java one, and it went something like this. Java dev - “How do you know when you refactor your app that if you change something in one place, it will not cause a problem in another distant part of the app, without the compiler to help you” And the clojure dev answers with “why would you ever write an app where one thing in one place changes another thing in another place?” I forgot where it was from though.

Anyway the sentiment is that clojure gives you tools to write an app like that where all the things that affect each other are close by, so it is actually possible to write big apps without types.

TS on the other hand gives you tools to write a big app where things _can_ affect different places, but you can still manage it. You can “fit” a bigger app in your brain.

JS by itself gives you no tools whatsoever so you’re at your own mercy, so you’re either more careful, or don’t write big programs and stay small. Or alternatively invent strategies that help organize things (redux, express, react).

TS allows you to ignore all that, write a horrible mess, and still be able to ship and be productive. It makes bigger messes actually possible.

Now I’m a huuuge fan of TS myself and never start a frontend project without it. And I also think it can make code, especially functional code much better and easier to reason about.

But we’ve lost the natural selection of JS projects. Before TS, if you’ve not setup your project right, with test, structure, policies and processes it was doomed the moment it grew to a certain size. You either stayed small, did it right or failed and refactored. Now … now you can actually keep going for much much longer with just TS helping you. And god forbid the TS wiz that sets it all up leaves and you’re left with untangling a mess. It’s a nicer mess, but a bigger one.


Yes thank you. This was the point I was trying to make.


No, my argument is that TS makes writing code easier and that easier does not equal to quality [0].

I have been writing code for more than 20 years. Personally, TS doesn't impress me. This is not meant as a criticism. Personally, I have found functional programming to be revelatory. I am shocked that is as been around as long as it has and that our schools don't teach it (or just mention it) and the majority of people in our industry as resistant to it.

"No matter what language you work in, programming in a functional style provides benefits. You should do it whenever it is convenient, and you should think hard about the decision when it isn’t convenient." -- John Carmack [1]

[0] "Simple Made Easy" Rich Hickey https://www.youtube.com/watch?v=LKtk3HCgTa8

[1] "John Carmack on Functional Programming in C++" http://sevangelatos.com/john-carmack-on/


"Types don't make you write better code."

I'm going to disagree with this big time.

Types allow much easier refactoring. I can refactor a large TypeScript in a few hours that wouldn't even be possible in a few weeks in JavaScript.

This makes continuously improving code so much easier.

It also allows rapid iteration during development. I can change function signatures and object shapes to improve my code, and even if those functions and objects are used prolifically throughout my source, I can do the updates in a few minutes.

Compared to JavaScript where many time I'm like "this would be so much easier if this function took a different parameter and this object was required to have this field, but it would take me 12 hours to change and even then there might be places I miss."

Even competent unit testing doesn't save you from these situations.

I remember the 2015 JS conf when Douglas Crockford listed "patterns" to "help" with the aforementioned issues but really didn't do much. Then went on to say "real" developers don't need TypeScript even though TypeScript SOLVED every issue he mentioned he was trying to solve. I had to shake my head that day.


> Types don't make you write better code.

I believe they do. There's the concept of "type-driven programming" for a reason, which is essentially taking test-driven development one step further.

But yeah, type-systems can also make things harder - but if you want real functional programming _and_ static typing, then maybe you should look into other languages, on the frontend that would for example be PureScript or ScalaJs.


This is an excellent point. I just finished reading the book “Domain Modeling mad Functional” [0] and it absolutely opened my eyes in terms of the power of strong types and functional design.

I believe that strong types can absolutely help to improve a code base, but they can’t do it alone. And they certainly won’t do much if one only associates strong type systems with intellisense.

I agree that a strong type system might make some things harder, but I think that’s because you’re design probably requires a bit more thought up front. I don’t know that I agree with your “real functional programming” statement though. JS lends itself to functional design really well. I don’t use TS, but as a superset of JS, I’d be surprised if it didn’t work as well in an FP design.

[0] https://pragprog.com/titles/swdddf/domain-modeling-made-func...


> I agree that a strong type system might make some things harder, but I think that’s because you’re design probably requires a bit more thought up front.

Unfortunately it is rather because, as of now, all but the most advanced & academic type-systems are incapable of solving even basic tasks without getting into the way.

Easy examples: Pick a statically typed language of your choice and implement a function, that takes any function A that returns a boolean and returns a new function that is just like A but returns the opposite result.

E.g.:

    fun even(input: Number): Boolean = ...
    even(2) // true
    fun opposite(function: ?): ? = ...

    fun odd = opposite(even)
    odd(2) // false
What are the types of the two question-marks?

Or: Try to write a function that executes SQL and, based on the SQL, returns a proper return type that contains type-information about what will be returned.

Then write a function that can model joins of two of these SQLs. I.e. the compiler must be able to calculate a new type structure, based on two other type structures.

Or: Implement a function that "slides" over a list and the sliding size window can be configured.

E.g.:

    fun sliding(size: Number, list: List[String], function: ? => ...) = ...
    list = List("a", "b", "c", "d", "e")

    sliding(2, list, (window: ?) => print(window)) 
   // prints (a, b) then (b, c) then (c, d) ...

    sliding(3, list, (window: ?) => print(window)) 
   // prints (a, b, c) then (b, c, d) then (c, d, e) ...
What is the type of the value that will be passed to function: ? => ... The type must depend on the given window-size. But almost all type-system fail to make this possible. Only so called value-dependent type-systems can do that, and some other's that have built-in special features for specific use-cases, but fail to generalize them (e.g. looking at you typescript).


I have read this book and recommend it other devs. It is a great example of using types effectively and writing good functional code. I have worked on several TS projects and none even had a glimpse of the patterns explored in that book. That is my point. TS doesn't make you write better code. If anything, it encourages the dev to document the types which is still no guarantee of quality documentation.

I have worked on several JS projects that are written functionally. They have been some of the high points of my web development experience. None of the TS projects I have worked have been functional. Maybe it's a coincidence.


I agree with you that our industry should spend more time training frontend engineers. Frontend is often treated as an afterthought, which ends up being very costly in the long run.

That being said, the addition of types was definitely not us chasing a popular framework. Etsy adopts tech really, really slowly. We had a pretty specific set of problems that types had traditionally solved in many other areas of the stack, so we evaluated JavaScript type systems and decided on TypeScript.

I don’t really think the “types make bad code, functional programming makes good code” argument here is made in good faith — there are good and bad codebase using any paradigm you can think of. Give TS a try, or at least let me answer some of your specific concerns about it.


I have worked on several TS projects. None of them were good IMO. I'm not blaming TS. I've seen numerous bad JS projects also. I'm not passing judgment on Etsy's code, only sharing my experience.

You are correct, there is good and bad code written in all paradigms. My point was that the energy we (as an industry) employ marketing, migrating, and educating on new languages/frameworks/tools might yield more substantial results if we focused on fundamentals.


How TS makes functional code harder?


Typing curried functions is hard. Ramda can't get it quite right.

https://github.com/DefinitelyTyped/DefinitelyTyped/tree/mast...


What is functional programming without a type system.


Can anybody who has done both compare TypeScript's gradual typing with Python's?


Opinion based on my experience: Typescript kicks Python typing to the curb.


They're pretty similar. The auto complete alone is worth adding type annotations.

Both have type stubs for third party packages that can make it easier to get coverage for your project.

My organization gradually migrated a ~300k line Python project to have full type annotations and it's so much nicer to work on.


Typescript is far far superior to Python's type annotations. It's not close.

1. Probably 90% of the JS ecosystem has type definitions. Python is more like 40% so you're constantly hitting issues with dependencies lacking types.

2. Python only describe how to write down the annotations not what they mean. So you have several different type checkers that sometimes disagree on quite basic stuff like whether a variable can change type or not.

3. There are some holes in the annotation system, e.g. you can't properly annotate `kwargs`. It's a really bad idea to use kwargs but Python programmers don't usually write the highest quality code, and they often end up using kwargs a lot, so it would be nice to be able to type them.

Don't choose Python.


Most of my experience is with mypy and TypeScript.

If your Python code relies a lot on dynamic dict objects instead of classes you'll have a rougher time. You have to use `TypedDict` which is not very expressive and syntactically verbose. This could be considered a good thing since you'll be pushed to use things like `NamedTuple`, `dataclass`es, or (a third-party extension) `attrs`. Most JS/TS code is probably using plain objects rather than `Map`s. The TypeScript syntax for expressing object types is extremely expressive, though annoyingly `Object.keys` and `Object.entries` are not precisely typed (For legitimate soundness reasons. Object types are "inexact", whereas in Flow you can express both "exact" and "inexact".)

mypy supports union types, but not intersection types, unlike TypeScript. The closest you can get is creating a `Protocol` (i.e. interface) that extends multiple `Protocol`s, but this can be cumbersome.

mypy's generics are strictly worse, both syntactically and expressively. In mypy you can express both co/contra-variance explicitly, whereas in TypeScript variance is implied by the readonly-ness of an object's properties and variance of its methods.

Both mypy and TypeScript have `Any`/`any` types, but only TypeScript has the safer `unknown` type. You can kind of use `object` in mypy for this purpose.

Both have a "bottom" type, "NoReturn" and "never", respectively.

mypy has decent support for classes and subclass type-checking. TypeScript doesn't truly understand sub-class relationships nor even prototypal inheritance.

Both are very configurable in terms of the strictness checks, but mypy's flags are more poorly documented and it requires more work to increase the level of strictness. Enabling "strict" in TypeScript enables most of what one would want.

Anecdotally, I would say TypeScript has better control flow analysis. With mypy you have to cast a lot more often.

The TypeScript ecosystem is much stronger. Most new packages have types as do major packages (located in DefinitelyTyped repo). Python's typing ecosystem is fairly poor. There are barely any packages in Typeshed and a lot of new packages are still untyped.

TypeScript has a dedicated team at Microsoft working on it with a public roadmap and milestones, though big features only come around one or twice a year. Python's type ecosystem is more design by committee. A lot of conversation happens over mailing lists, monthly video calls, and a yearly conference. Since there are multiple Python type checker implementations, there isn't a single unifying vision. The core mypy team is employed by Dropbox, who has them spending most of the time migrating the giant monorepo from Python 2 to Python 3.

The TypeScript team is fairly decent at fixing true bugs between releases, but a lot of "bugs" are really feature requests that tend to become long-standing Github issues. On the other hand, last I checked, there are still some long-standing bugs in mypy that haven't been fixed (as not to spread FUD, I will say that they tend to be very edge-casey and more for power users).

Note that there are multiple type checkers for Python at this point with varying capabilities, but they share the same syntax and basic types since they're built into the language runtime. I expect Pyright (also from Microsoft) to come out ahead as more people start using the Pylance VSCode extension. mypy's editor/IDE support is not great, based on my VSCode experience. I haven't tried it with PyCharm.


> TypeScript doesn't truly understand sub-class relationships nor even prototypal inheritance.

Yes, I was recently spent several hours trying to write a conditional type based on the prototype of a generic type and gave up in the end. It's a surprising gap in one sense, but on the other hand I've never needed it before and probably wouldn't have needed it then were I not fiddling around with some toy code in the TS playground.


Typescript is the language that will put a lot of other server-side-only languages to bed when it comes to web-application back-ends (and then it will seep into everything else, unless it's performance critical, but at some point that'll probably be addressed, too).


I don't think so.

Typescript is a decent language, but severely suffers from the javascript compatibity. On the frontend that does not matter as much because you usually don't deal with critical data persistence, but on the backend it's different.

If what you said was true, Javascript would have already taken over the backend, but it hasn't. There are two reasons why:

1. There are still languages on the backend that are more productive / powerful / mature than typescript.

2. On the frontend side, the hurdle of getting into things is much lower. That's why, on average, folks working on the backend are usually more involved and have a broader exposure the languages.

That being said, there are surely languages, I would love to see replaced by Typescript (for example looking at you, Java). And while it might look like Typescript could do it from someone who uses it for frontend, I'd say it won't happen.


Typescript is amazing for frontend development, but I don't understand why anyone would use it on the server side?

The point of TypeScript is it extends JavaScript in a backwards-compatible way and still compile down to JavaScript which will run in any browser. It cant make the quirks and design mistakes of JavaScript go away, but it can protect the developer against them.

But on the server side there is no compatibility issues, so why not just use a well-designed language in the first place? C#, Python, Go...all of them are much better designed than JavaScript.


People who've been writing only JS for years descover typing and suddenly think it's the holy grail.


Yeah while they liberally apply ‘any’ all over a codebase.


I’ve just accepted this is true (for JS or TS). I spent years avoiding using JS but have just come to accept I need to crack on with it regardless, it’s here to stay and probably just going to grow. No point severely harming my job options over technical preferences.


Sad but true.


i sure wouldn’t mind seeing typescript become a dominant language in back-end development


I love using Typescript, but I only ever use interface and enum. What extra benefits would I get from using other features than those? What am I missing out on? :)


Tagged union types [0] are very powerful and let you model most things in very robust ways.

   type Response<Data, Error = string> =
   | { type: "Loading" }
   | { type: "Success", data: Data }
   | { type: "Error", message: Error }
You won't have access to data unless type === "Success". You can model other mutually exclusive situations in the same way.

Then, Typescript comes with very handy utility types. [1]

And if you're having an especially good day, you can loose yourself in type level programming. Mapped types, for example. [2] Though this is only really necessary at a library level.

Back in reality, type narrowing [3] and function overloading [4] are good to know about.

And there's a lot more.

- 0 https://mariusschulz.com/blog/tagged-union-types-in-typescri...

- 1 https://www.typescriptlang.org/docs/handbook/utility-types.h...

- 2 https://www.typescriptlang.org/docs/handbook/2/mapped-types....

- 3 https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...

- 4 https://www.typescriptlang.org/docs/handbook/2/functions.htm...


IMO you should ditch enums (just use union types 'value1' | 'value2' etc...) and stick to writing "statically typed javascript". You'll get enough value from it.

Incomplete and (very) biased list of why I find this approach useful:

- Amazing static analysis capabilities

- Being able to design the code by writing types first and just filling the gaps between them

- Less code which needs to be covered with unit tests

- Autocomplete for any 3rd party library (e.g. AWS, or try spiking some backend code with Contentful and navigating their data structures, it's sooo much easier once you describe your models using TS)

- Less thinking about "how" and more about "what" I want to build

- Still reads like JS so the barrier of entry for colleagues is easy

I'm not a big fan of interfaces, but I understand why they exist (declaration merging/patching mutable JS scopes/objects).

Edit: I just realised that I got overly excited and perhaps went too broad with the list of pros, but hope this is still useful. The official TS documentation says that you should default to interfaces instead of types, but the approach that worked really well for me is to start with types and use interfaces only when really necessary. Somehow this model fits better in my head, but that's maybe because my coding style is more functional than OOP.


I really don't understand how the dinosaurs see things. You can be using something for 5 years and see the benefits before you're reading an article about how X company started using and benefiting from it.

Can't wait to be reading in 5 years how Graphql was better for a company using SOAP


Because they were busy making money instead of worrying about the tech.


Great in-depth write-up!


Thank you! Very much appreciated.


The real purpose of TypeScript is job creation. Corporations constantly need new ways to keep their employees busy, distracted, and compliant. I really hope that one day, most developers will realize that there are far more important things than static typing... I mean so much more important that it makes static typing seem completely futile and even counterproductive.

You may downvote today, but you might understand what I mean in 5 to 10 years. I've been through all the phases already. I'm even exhausted trying to explain it to people. The hype is too strong. Good luck, gen z.


> The real purpose of TypeScript is job creation. Corporations constantly need new ways to keep their employees busy, distracted, and compliant.

You threw out a wild thesis and then said nothing to support it. Can you elaborate and actually provide evidence for what you're referring to and why Typescript is a trojan horse for useless busy work like you're alleging?

At our org Typescript has been a game changer for and by developers. It makes our lives better. It was driven by us, for us. And we had to sell it to management, who reluctantly OKed it knowing it was going to cost the org money but make us happy. How does that fit with your thesis?


I'll downvote today because cryptic "you'll understand when you're older" comments that don't expand on why you believe that to be the case add very little to the discourse, even if I might agree with the overall premise.


The gen z people I know think webdev is a joke. They’re off playing with unreal engine blueprints and shaders and stuff.

TS on the serverside is an abomination. Just learn a real language like Go or hey Java.


Interesting that you mention it. I'm much more versed in TS than in go or java for sure, but after some knowledge sharing sessions with devs in those languages, as well as Scala devs, I can say they all _can_ learn a thing or two about types from TS, even if they themselves are superior on the backend.

For example I was surprised to realise how TS took go's value based interfaces and applied it to java concepts. And added almost Haskel level type manipulation on top of it.

These are rather recent developments - last couple of years stuff, but in TS you can write things closer to higher kinded types, conditional / dependent types and all sorts of wizardry that can explain to the compiler what _exactly_ you want to accomplish, much more so than in Java or go. And you have a very cheery and down to earth language underneath to do it with.

Once you do, you almost don't need unit tests - you get the "if it compiles, it'll work" kind of feeling. I've had sessions of days writing TS code without ever running it once, completing a complex feature, then compiling it and having it work exactly as specified on the first try, it's bizarre.

Now I'm not saying you can't do that with go or java, its just that I have a feeling people are dissing TS for what it was 5 years ago, and haven't realised they've been passed over already.

TS's biggest downside compared to langs like Scala is that you can't use typescript types at runtime, where apparently Scala gets its more advanced features (implicits etc). Oh oh and if it ever gets pattern matching and algebraic effects ...

And if deno's creators play their cards right and fix the standard library, give us go's channels, scala collections or swift's actors ... one can only dream.


Would it be possible to fork Deno and expose types at runtime? My backend collegues seem to desire it.


You could probably codegen the .d.ts output of your types into different the body of API responses (or use it to generate a json schema), forking deno for it is probably overkill



If you've been to that much effort already, one imagines you'll have a canned summary of your thesis and arguments somewhere linkable. I mean, of course there are far more important things than static typing. Which of them do you have in mind?


Handling of state. Reusability, extendability, flexibility, immutability, avoiding brittleness and coupling.

The things Typescript gives you are very physically visible and so, gives the illusion and confidence it will guard you in everything but in reality it helps in the most trivial of problems and you are paying a big cost for it.


I've been writing Javascript for twenty-six years, seventeen professionally, and Typescript since 2017. No one has ever in my experience made the claim that Typescript solves all, or even really any, of the concerns you list. That when well used it can make them easier to solve, sure, and that's true. It comes in exchange for a little more effort, that's also true, but I've found that tradeoff considerably beneficial on net, because the type system when used thoughtfully makes whole classes of bugs harder to write, and their virtuous complements easier.

If people are saying the type system is a magic bullet, the problem is with that claim and the people making it, not with the type system.


What is the cost?


High cohesion, loose coupling.

That summarizes it. But to understand what it means usually takes at least 10 years and most people will never understand it.

My best interpretation of high cohesion is that it means that every component/module/class in a system can be easily explained to a non-technical person who has business/domain knowledge... In as few words as possible.

Loose coupling requires simple interfaces. Simple functions/methods which accept simple (raw primitives or plain objects) as arguments... You want to avoid methods which accept 'live instances' as much as possible. Instances which have their own internal state should not cross module boundaries ideally (not passed around as arguments). That is also a sign of low cohesion. High cohesion and loose coupling often go together naturally. For loose coupling, I often think of a power plug/power point or a USB port... Simpler is better. Imagine how difficult it would be to plug an elaborate 10-prong power plug into a socket with 10 holes... Imagine how much work that would be for the electronic equipment manufacturers. The interface must be as simple as necessary to get the job done.

If you can do these two things, it will constrain how you think about and write code and you will produce the most maintainable code you can imagine. The language won't even matter at all. If your method interfaces are simple (no complex type parameters), it becomes trivial to figure out which types a specific methods accepts as arguments.

IMO TypeScript makes it easier to work with bad code, but isn't a better solution simply to avoid bad code? It's not that difficult once you have the right approach.

In a way, I feel like TypeScript is a symptom of the entire industry surrendering to bad code... A surprising number of people in the corporate sphere actually believe that there is no such thing as good code and even that architecture, software design and planning doesn't matter. The only solution they will accept is to throw more people at the problem and implement security through obscurity.


> Imagine how difficult it would be to plug an elaborate 10-prong power plug into a socket with 10 holes...

Imagine how difficult it would be to plug an elaborate 10-prong power plug into a socket with unknown number of holes.

With static typing, you at least know how many holes are there.

Now, I agree that "the interface must be as simple as necessary to get the job done", but non-trivial problems will have non-trivial essential complexity even after you have removed all incidental complexity.


Yes but you forget incentives. If a tool makes something easier. People get lazier.

E.g.

People today have terrible handwriting because of computer keyboards.

Many people today can't do long division on paper because of calculators.

People today struggle to find their way around new places when they don't have Google Maps.

But in this case, the skill which is being atrophied is an actual useful skill... Though it's not obvious at a glance. A tool which makes it easier to do the wrong thing will lead more people to do the wrong thing.

The friction which dynamic languages create is a positive and promotes good code and professional development.


Are you arguing that we should know which roots contain water and which parts of a horse are best for making bow strings? Circumstances change, skills evolve with the circumstances. Yes, there is a danger of sudden reversal of circumstances, but we still have our ability to relearn and rediscover the "old" skills if need be.

If friction was "positive and promoted good code", the best code would be on punch cards.


I understand that we are not used to factor in psychology when it comes to software development and that this argument may seem contrived but I'm speaking from my own professional experience when I say that I've observed both in myself and others that learning to use dynamically typed languages correctly has had a significant positive impact on the general code style. I've gone back and forth between the two paradigms multiple times. It forces developers to pay more attention to software design and structure or else it becomes overwhelming. The fact that code becomes overwhelming more quickly with dynamically typed languages is a positive thing. It's an effective deterrent against complexity and incorrect abstractions.


I don't agree that types make it easier to write bad code. If anything, they make it easier to spot bad code by making interfaces more explicit.


> symptom of the entire industry surrendering to bad code

Exactly. That's why TypeScript is structural typing with inference. The industry has given up on teaching developers how to organize code. The only way to force everyone to play nice together is through tools: prettier, VSCode, TypeScript, lint, etc. etc.

Part of me thinks this is a social problem. Too many unmoored egos in the industry and no one in the social position of being able to lay down ground rules for a codebase. Or possibly a result of devs chasing higher salaries and becoming mercenaries always on the move. It's rare to have teammates that have been with a company longer than a year anymore.


TypeScript also makes it easier to work with good code.


What’s an example?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: