TypeScript is currently getting lots of love from people, but there is also a lot of misunderstandings surrounding it. That’s not to say that there aren’t good, legitimate critiques; but, ultimately TypeScript is helping a lot of people. As a longtime lover of JavaScript and as someone who is constantly researching how to improve my communication skills, I’ve started to notice an overlap in these two problem spaces. So for a moment, allow yourself to see how TypeScript is enabling developers to code in an expressive, enjoyable way that introduces calmness and security to the codebase.
In this series, we’ll compare and contrast how TypeScript helps solve conversational “anti-patterns” that most of us struggle with.
Asking for help upfront
“Never mind, I don’t need any help.” You’d never want a friend to hold back their feelings like that, so why would you want your software to deny help? And yet it happens. Sometimes people acquiesce and fall into a “martyr complex” where they try to be “strong.” They try to make the best with what they were given. But in the realm of software, it becomes easy to see how this problem of “not communicating your needs” can cause problems. Would you rather get the following error at runtime, or would you rather find out right away in your code editor?
Many of you reading this might think, “Why would I care about static typing at compilation. I’ve been writing NodeJs with Mongoose for years.” Me too. And after 8+ years of relying on runtime exceptions, I’m looking to be more efficient. I want less resistance:
- I’ve grown tired of retrying my request in curl/Postman, over and over again…
- I’ve grown tired of refreshing the UI to see what mistake I’ve made, over and over again…
It works, but there is a better way. Whenever I get a surprising error back from someone else’s function, I think, “wow, I really wish I’d known that sooner.” I’m reminded of this scenario from Psychology Today:
Think about the bosses, co-workers, friends, teachers, and colleagues you may have had over the years who simply were not good at communicating. You may have learned, months or years later, about something you did to offend them or something important they neglected to share. People are hurt when the truth comes out later, but why is telling the truth at the time such a hard thing to do?
It hurts more to find out that what you coded isn’t actually going to work. And Agile development is all about faster feedback loops (read why here). So isn’t it better to know up-front? Let’s rewrite the example above so the developer can find out right away:
It’s a simple change to the code, but it provides enormous value to the consumer who doesn’t have to worry about run-time errors caused by ambiguity about how the code works.
What Buddhism Would Say About TypeScript
Now that I’ve shown the technical approach on how to be clear and upfront about your needs, let’s consider the value of mindful communication:
“Aware of the suffering caused by unmindful speech and the inability to listen to others, I am committed to cultivating loving speech and compassionate listening in order to relieve suffering and to promote reconciliation and peace in myself and among other people, ethnic and religious groups, and nations. Knowing that words can create happiness or suffering, I am committed to speaking truthfully using words that inspire confidence, joy, and hope.
~ Thich Nhat Hanh
What does Thich Nhat Hanh mean when he says to “inspire confidence” through mindful communication? I would argue that confidence comes from communicating clearly and directly. If you clearly state your needs (your inputs) and you clearly state what you will do (your outputs) then there should be no ambiguity about how another coworker (or piece of software) can interact with you. With TypeScript, it’s so incredibly simple to clearly state what your requirements are. By doing that, your code will continue to push uncertainty upward. Your code becomes clearer as you continue to removing doubt. Doesn’t removing doubt sound nice?
Now, I realize that the example above is a simplistic example (don’t worry we’ll ramp up quickly in the next article), but if you take this approach and continue it from the bottom of the stack to the top, and you’ll find that any and all ambiguity has been relegated to the top of the stack. In other words, the only place where bad data can come in is:
- Where the user enters data into the UI (i.e. a form input)
- And you can solve that with form validation
- Where your service serializes the HTTP request into a strongly-typed object
- And you can solve that with runtime schema validators like tsoa or io-ts which keeps those runtime types (JSON Schema) in-sync with the compile time types (TypeScript definitions)
So if you can keep that honest communication all the way up to the top, then you prevent the “garbage in garbage out” problem.
In our next article, we’ll use TypeScript to tackle the communication problem of “saying what you mean.”
This is a series on communication anti-patterns and how TypeScript can solve similar problems. Follow along on Twitter or dev.to so you can see how our code gets clearer and more expressive as we go. After all, as Thich Nhat Hanh says, “Freedom is the basis of all happiness. Without freedom, there is no happiness.”
Top comments (9)
In your example, you mention that with typescript "this will never happen"
Seems like Typescript helps reduce the chances of this happening, but it is actually a different check.
Typescript will tell the developer that lastName is required, but lastName could be an empty string at runtime. If it's important that lastName is not empty, you will still need the
!newUserToSave.lastName
check in your code.I've been looking at adopting Typescript, but this is one issue I have with Typescript right now. The data I'm dealing with is legacy data and has lots of inconsistencies. The only way to catch stuff like this with the data I have is at runtime. It does seem valuable to know up front that lastName is a require property, but because it doesn't do runtime checking you can still get some invalid states.
Short version: just use io-ts or tsoa to help check for this at runtime.
Long version: You make a very good point however there are no programming languages that check the length or existence of a string at compile time. So in Java, modern C#, Kotlin, etc a string can still be empty. However, TypeScript and Kotlin can at least check for null and undefined unlike Java and older C#.
Think of this like an array of characters instead of a word. In fact, that’s how C++ represents strings (from what I can remember).
So since the length of an array is always a variable size the language can’t determine if the string is empty at compile time. But if you use a runtime validation library at your io layer or your API layer then you can clean the data before it gets too far into the code.
Thanks! That's a good point that no other language handles this case either. I'll look into io-ts and tsoa.
Mention of io-ts (github.com/gcanti/io-ts)! :D
Put it on every network calls and storage access and suddenly your apps is resilient af
Yea! That’s a great library! :) Have you tried tsoa yet for getting runtime checks at the front door of your APIs?
Not yet.
Seen tsoa's docs. Didn't get a chance to play with it since most of my time coding is done for proprietary code.
I was kinda intimidated on using it because of the decorator (not much of a fan) pattern and also controller class pattern.
So, didn't get a chance to put it on the roadmap too.
Is it that good and worth to try?
Oh yes, it’s definitely worth a try. And yes, I’m also not a fan of classes (in general), but as far as I know tsoa is the only library that supports swagger generation and validation for TypeScript. So I’d definitely check it out. Also, if you ever want to transition to C# you’ll find that the decorators are almost identical to what you’d find in .NET Core.
Wait, shouldn't the check
if(newUserToSave.lastName)
be
if(!newUserToSave.lastName)
? :D
Thank you so much for finding that and pointing it out. I've corrected the example. Ironically, you kind of illustrated how easy it is to make a mistake in JS because I wrote that example without VSCode's help. If I had written that example in TypeScript I wouldn't have been able to make the mistake.