Why every new web app at PayPal starts with TypeScript
What happened that made TypeScript viable for me and worth migrating paypal-scripts for.
I’ve been using TypeScript for ~1 week now. Migrating from Flow.
Breath. Of. Fresh. Air.
I’ll blog about this in a week or two. This is really great though. – Dec 20, 2018
… here it is!
At PayPal, I work on a tool called paypal-scripts
which is a toolkit (like react-scripts
from create-react-app
, or angular-cli
, or ember-cli
or … etc…). I’ve written about it before. The idea is that it encapsulates all the tools common to PayPal applications and published modules. The goal being taking the huge list of devDependencies
in the package.json
and all the config files and reducing that down to one entry in the devDependencies
. And because all the config lives within a single opinionated package, keeping things updated is a matter of updating one dependency (paypal-scripts
) which typically does not need to make breaking changes. So you just keep that one dep updated and you go back to building your app.
For the last year, people at PayPal have opted-into adopting paypal-scripts
. At PayPal, you create a new app by clicking a few buttons in a web UI which will create your GitHub (enterprise) repo and setup CI, deploys etc. etc. The GitHub repo it creates for you is based on a repo called the “sample-app.” Just last week, my PR to update it to use paypal-scripts
was merged. This means that every new applications at PayPal will get their start with modern technology and tools that they don’t have to worry about keeping up to date. They will also be statically typed with TypeScript and tested with Jest.
Honestly, this is my Magnum Opus of my career. I honestly don’t think I’ll be able to top this at PayPal. The impact of this project is huge and I’m so grateful to PayPal for giving me the opportunity to work on something so huge.
/you shakes me awake
Right ok, back to the TypeScript thing.
So halfway through December, I was working on getting the sample-app updated to use paypal-scripts
. I was also working on pp-react
which is a (WIP) reusable component library for PayPal projects (buttons, modals, typography, etc.). Because paypal-scripts
supports publishable modules, I was using paypal-scripts
to build pp-react
. A month ago, paypal-scripts
shipped with support for FlowType. Support for FlowType was really easy to add to paypal-scripts
thanks to Babel.
On December 12th, as I was working on Flow for both pp-react
and the new sample-app
, I finally got fed up with Flow (more on this later) and made a snap decision. I sent this to my co-worker Jamund Ferguson:
What would you say if I tried to make the sample app use TypeScript
To which he responded with:
YES DO IT
And so I took a poll of the #paypal-scripts
slack channel and 100% of respondents said they wanted the change. That was good enough for me and I started the work on it. About a week later, I had totally migrated paypal-scripts
from supporting Flow to supporting TypeScript (most of that time was making all the tools recognize .ts
and .tsx
files 🙄 and allowing paypal-scripts
to dogfood itself which is kinda tricky 🐶). Then a few days of updating my sample-app
PR to use the new and improved paypal-scripts
and move from .js
to .tsx
and .ts
files. Then we had Christmas break 🎄 and the week we got back after the new year 🎆, it was merged and now every new project starts off with modern tools that will stay updated by default and will be statically typed with TypeScript.
NOTE: Of course once people create their app, they are free to do whatever they like with it, they can remove all the code and switch to Elm or whatever and that’s totally fine. But most projects will stick with what they’re given thanks to the default effect.
What took took me so long?
Alright, so this is a question I get a lot from TypeScript fans. My head’s not been in the dirt. I remember evaluating TypeScript in ~2013 when a co-worker tried to convince us to adopt it in our 500k line codebase (he failed, but TS was pretty young at the time so I’m not too sad about it). I even interviewed Anders Hejlsberg (TypeScript creator) on JavaScript Air 2 years ago.
Here are the things that held me back from TypeScript:
Abandoning my existing toolchain of Babel and ESLint
This was definitely the biggest plus for Flow over TypeScript for me for many years. I’ve been using these tools for years and really enjoy building custom plugins for both Babel and ESLint (and you can too!). I love the huge community around both of these tools as well and I don’t want to give them up. Up until recently, I would have had to give them up if I wanted to use TypeScript. Sure there’s TSLint, but ESLint’s community is WAY bigger.
One thing I like about Flow is that all I need to do to adopt it is:
- Add the babel preset for the syntax
- Add
// @flow
to the top of every file I want to typecheck (there’s an eslint plugin to make sure you do) - Add a script to run flow on the codebase to do typechecking
I really like that typechecking (via Flow) and code building (via babel, webpack, or rollup) are separate. I didn’t want to hand my life over to TypeScript, particularly if it wouldn’t understand my custom babel plugins anyway. Particularly because I had flow which was “good enough” (read more about this below).
From there, everything continues to work like normal. Well, thanks to Babel 7 (and more specifically @babel/preset-typescript
), we can keep our tools and get most of TypeScript as well. The biggest trick is making tools accept the .ts
and .tsx
extensions, but thankfully that’s a solvable problem.
Forcing contributors to have to know TypeScript to contribute
I’m mostly talking about open source stuff, but this applies to work stuff as well. Though I always felt like work stuff should be typed and we were covered by Flow, I held myself back from adding it to my open source stuff for this reason. It’s one argument that I always told myself, but in the back of my mind I always had a rebuttal to myself: “Types are basically another form of testing which people have to learn to contribute as well. So they’re going to have to know or learn it to contribute anyway.”
It’s a pretty poor argument honestly and with more and more people learning TypeScript I think I’ll probably be authoring my open source packages in TypeScript in the future.
Flow Type Inference
I read and loved this blog post from Jamie Kyle. Especially the last line here:
With Flow you’ll be adding types to make errors nicer, not to uncover them.
This is absolutely true. Today, Flow has better type inference than TypeScript and that comforted me.
Flow is from Facebook just like React
I’d be lying if I said I didn’t succumb to this all-too common mistake of assuming that because a company was pushing one technology that I think is a real winner the other technologies must automatically be the best too. That’s not a given. That’s all I’ll say about this…
TypeScript Zealots 😬
Ok, so I think we can agree that when people are really excited about a technology they can’t stop telling people about it. Anyone here use vim? My experience is that TypeScript is no exception. The TypeScript community is full of wonderful people who are super kind, helpful, enthusiastic, and friendly.
But I’ve had interactions with people who would call you crazy or stupid for not using or understanding TypeScript or using anything else. That attitude is free of empathy and pretty snobbish. That is not the kind of community of which I want to be a part. I mean, it’s great to be enthusiastic about your technology choice, but it can go too far when you start insulting other people who have made different choices.
Your impassioned love of TypeScript is sorta freaking me out. – Jan 5, 2019
This is still a concern that I have. Hopefully we can work together to improve the empathy of the community.
What was so wrong with Flow?
Ok, so as I said, I was fed up with Flow. Here’s one tweet that sums up my biggest issue with flow:
Am I the only one that has a script called “fflow” that runs: “flow stop && flow”
Pretty regularly if I run
flow
by itself it’ll fail, but if I stop flow, then run it again it’ll magically work… Am I alone in this?
Here’s another that’s more specific:
I love your enthusiasm @flowtype, but these look like errors to me 🤔
The regular unreliability of flow was what finally made me give up on it. The editor plugins only sometimes worked (full disclosure, I never tried Nuclide and maybe my life would’ve been different if I had, but I tried flow in Atom and VSCode) and I would get issues like the one all the time. It was incredibly frustrating because I could never trust my type checker. There were other issues as well.
When I saw this tweet thread from Jamie Kyle back in November, it really resinated with me and my experience. I honestly couldn’t stop thinking that I should really give TypeScript a solid shake. So I finally did and I’m glad that I did!
Other questions and answers…
Why not TSLint?
I actually implemented TSLint in paypal-scripts
. It was one of the first scripts that I got working. I would detect whether to use TSLint or ESLint based on whether your project had a tsconfig.json
file. But then I remembered that we actually have some custom ESLint plugins (for example, some for internationalization) that I didn’t want to spend time rewriting as TSLint plugins. Also, the TSLint CLI is not quite as capable as ESLint and it didn’t really work nicely for the way that paypal-scripts
work. I may investigate TSLint again in the future. Oh, and the ESLint community is still WAY bigger. I’ve also been slowly been discovering that a reliable typechecker makes linting plugins useless. But for now using ESLint with TypeScript is not bad at all. And check it out, I show you how to do it in this DevTip.
Why not Reason?
In the tweet referenced above, my co-worker Jamund tweeted:
It may be time to try@typescriptlang!!!
To which I responded:
Honestly, if I’m going to make a change, it’ll be @reasonml :P
I’ve often said that I’d switch to Reason before switching to TypeScript. A big reason for this was what I mentioned above about having to abandon my existing tools. But because I wasn’t forced to face that, TypeScript was more attractive. I’m still excited by Reason, but switching to Reason would have been a HUGE jump for lots of people at PayPal and while I think they’re super smart and capable people, I think they’ll be more productive using TypeScript than trying to learn a new language.
What would have happened if I’d gone with Reason is I’d probably never have gotten my PR merged into the sample-app. It’s one thing to get buy-in from folks about using something that’s basically JavaScript with types (especially when there’s basically no config to maintain), but it’s an entirely different conversation to convince them to use an entirely different language and ecosystem (no matter how good that language’s inter-op with npm/JS is).
Thanks
I want to say thank you to all you people on Twitter who have helped me learn a bunch about TypeScript these last few weeks. You’ve been super and I hope to pay it forward in the form of learning materials for the next wave of TypeScript learners.
Thanks! – Jan 5, 2019
Seriously, twitter people helped me a LOT as I was making the adjustment, so thank you! I am going to try to make stuff to help people learn TypeScript. Here’s one thing I made recently based on stuff I learned from twitter folks: TypeScript: Why you have to add types even if you handle the undefined case.
Conclusion
As I said, getting paypal-scripts
into PayPal’s sample-app is probably the thing I’m most proud of from a career standpoint. It’s going to make a HUGE impact. The fact that I was able to slip in TypeScript support at the very end there is a HUGE win for PayPal employees. I’m really happy about the decision to adopt TypeScript.
Things to not miss:
- devpath.fm - A new podcast (I’m on the first episode) about developer career paths 🎙️ hosted by Jacob Herrington. It’s got some really awesome episodes already!
- Add Cypress to a gatsby app - The gatsby app I’m adding Cypress to is my rewrite of kentcdodds.com and it’s built with TypeScript :)
- testing-library.com - dom-testing-library, react-testing-library, cypress-testing-library, etc. etc. all now have a unified documentation home!
Some tweets from this last week:
The sole determining factor in whether you were a jerk is your actions. Whether the other person “deserved it” does not magically change anything. Don’t be a jerk.
The community 😏
https://twitter.com/nodejs/status/1081319241934991360
This week’s blog post is “Light the World ✨”. It’s the published version of my newsletter from 2 weeks ago. If you thought it was good, go ahead and give it some claps (👏x50) and a retweet:
I just published “Light the World ✨”
https://blog.kentcdodds.com/light-the-world-ea1722e0d400
P.S. If you like this, make sure to subscribe, follow me on twitter, buy me lunch, support me on patreon, and share this with your friends 😀
👋 Hi! I’m Kent C. Dodds. I work at PayPal as a full stack JavaScript engineer. I represent PayPal on the TC39. I’m actively involved in the open source community. I’m the creator of TestingJavaScript.com and I’m an instructor on egghead.io and Frontend Masters. I’m also a Google Developer Expert. I’m happily married and the father of four kids. I like my family, code, JavaScript, and React.